summaryrefslogtreecommitdiff
path: root/protocols/Telegram/tdlib/td/tdutils
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/Telegram/tdlib/td/tdutils')
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/CMakeLists.txt214
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/generate/CMakeLists.txt20
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/generate/generate_mime_types_gperf.cpp22
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/generate/mime_types.txt28
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/AesCtrByteFlow.h26
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.cpp160
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.h51
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/AtomicRead.h89
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.cpp104
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.h30
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedFd.h62
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedReader.h16
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.cpp17
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.h177
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ByteFlow.h139
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/CancellationToken.h71
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ChainScheduler.h376
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ChangesProcessor.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Closure.h84
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/CombinedLog.h86
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ConcurrentHashTable.h322
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Container.h7
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Context.h44
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/DecTree.h216
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Destructor.h52
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Enumerator.h20
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/EpochBasedMemoryReclamation.h201
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.cpp20
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.h30
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.cpp124
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.h33
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMap.h24
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMapChunks.h575
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashSet.h24
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.cpp24
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.h551
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlFast.h81
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.cpp32
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.h35
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlStrict.h38
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.cpp59
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.h20
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.cpp83
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.h7
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Hash.h70
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/HashMap.h27
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/HashSet.h27
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/HashTableUtils.h72
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/HazardPointers.h54
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Heap.h67
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.cpp125
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.h21
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.cpp134
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.h43
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.cpp100
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.h230
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/List.h63
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MapNode.h166
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MemoryLog.h60
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.cpp2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MovableValue.h16
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.cpp15
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.h56
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcWaiter.h281
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MpscLinkQueue.h13
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/MpscPollableQueue.h65
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Named.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/NullLog.h19
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ObjectPool.h14
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Observer.h6
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.cpp263
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.h81
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/OptionsParser.h150
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/OrderedEventsProcessor.h16
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Parser.h104
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.cpp70
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.h57
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Promise.h373
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Random.cpp119
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Random.h45
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ScopeGuard.h15
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/SetNode.h129
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/SharedObjectPool.h52
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/SharedSlice.cpp (renamed from protocols/Telegram/tdlib/td/tdutils/td/utils/GitInfo.cpp)13
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/SharedSlice.h382
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Slice-decl.h49
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.cpp34
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.h90
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/SliceBuilder.h57
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Span.h158
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/SpinLock.h8
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.cpp72
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.h79
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Status.cpp40
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Status.h253
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/StealingQueue.h125
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Storer.h22
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/StorerBase.h4
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.cpp151
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.h75
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadLocalStorage.h55
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadSafeCounter.h132
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Time.cpp36
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Time.h50
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TimedStat.h36
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.cpp41
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.h19
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TlDowncastHelper.h29
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TlStorerToString.h180
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.cpp64
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.h33
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.cpp106
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.h23
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TsList.h214
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.cpp21
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.h55
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/UInt.h91
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/Variant.h28
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/VectorQueue.h94
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashMap.h190
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashSet.h141
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeVector.h69
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/algorithm.h217
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/as.h81
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/base64.cpp289
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/base64.h11
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/benchmark.h18
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/bits.h310
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.cpp118
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.h206
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/check.cpp23
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/check.h32
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/common.h45
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/config.h5
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/config.h.in5
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.cpp1115
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.h143
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.cpp302
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.h32
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.cpp96
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.h22
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.cpp8
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/fixed_vector.h79
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/format.h68
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/int_types.h22
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/invoke.h72
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/logging.cpp271
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/logging.h244
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/misc.cpp200
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/misc.h179
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/optional.h71
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/overloaded.h4
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.cpp81
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.h19
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/CxCli.h65
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFd.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFdBase.h8
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.cpp1104
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.h226
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.cpp481
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.h50
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/FromApp.h100
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.cpp473
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.h52
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/IoSlice.h42
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.cpp109
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.h52
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Mutex.h30
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Poll.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollBase.h11
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.cpp71
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.h122
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/RwMutex.h10
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp384
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.h35
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.cpp683
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.h53
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.cpp142
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.h26
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.cpp251
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.h49
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.cpp873
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.h93
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/config.h21
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.cpp82
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.h30
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.cpp45
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.h26
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.cpp94
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.h29
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.cpp41
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.h24
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.cpp110
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.h71
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.cpp115
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.h38
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.cpp261
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.h68
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.cpp44
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.h24
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/PollableFd.h230
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.cpp49
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.h27
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.cpp34
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.cpp217
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.h83
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadStl.h99
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.cpp70
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.h28
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/skip_eintr.h63
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.cpp304
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.h223
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.cpp25
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.h29
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.cpp97
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.h20
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.cpp88
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.h6
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.cpp2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.h2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.cpp139
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.h23
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread.h5
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.cpp12
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.h18
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/uname.cpp289
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/uname.h (renamed from protocols/Telegram/tdlib/td/tdutils/td/utils/GitInfo.h)8
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.cpp57
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.h16
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.cpp111
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.h4
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/queue.h40
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/tests.cpp271
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/tests.h258
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/tl_helpers.h148
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.cpp38
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.h98
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/tl_storers.h211
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/translit.cpp115
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/translit.h16
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/type_traits.h25
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/uint128.h293
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.cpp1484
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.h9
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/unique_ptr.h106
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.cpp117
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.h48
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/ChainScheduler.cpp244
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/ConcurrentHashMap.cpp252
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp2
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/EpochBasedMemoryReclamation.cpp68
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/HashSet.cpp438
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp16
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/HttpUrl.cpp54
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/List.cpp169
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp24
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp60
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp17
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/OptionParser.cpp82
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp6
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp25
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/SharedSlice.cpp91
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/StealingQueue.cpp180
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashMap.cpp95
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashSet.cpp73
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/WaitFreeVector.cpp69
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/bitmask.cpp249
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/buffer.cpp56
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp333
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/emoji.cpp128
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp68
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp188
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/hashset_benchmark.cpp647
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/heap.cpp47
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/json.cpp34
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/log.cpp187
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/misc.cpp1218
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/port.cpp316
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/pq.cpp138
-rw-r--r--protocols/Telegram/tdlib/td/tdutils/test/variant.cpp24
283 files changed, 27488 insertions, 5818 deletions
diff --git a/protocols/Telegram/tdlib/td/tdutils/CMakeLists.txt b/protocols/Telegram/tdlib/td/tdutils/CMakeLists.txt
index 1fbc34df32..7c568fdeba 100644
--- a/protocols/Telegram/tdlib/td/tdutils/CMakeLists.txt
+++ b/protocols/Telegram/tdlib/td/tdutils/CMakeLists.txt
@@ -1,4 +1,12 @@
-cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
+if ((CMAKE_MAJOR_VERSION LESS 3) OR (CMAKE_VERSION VERSION_LESS "3.0.2"))
+ message(FATAL_ERROR "CMake >= 3.0.2 is required")
+endif()
+
+option(TDUTILS_MIME_TYPE "Generate mime types conversion; requires gperf" ON)
+
+if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
+ set(CMAKE_INSTALL_LIBDIR "lib")
+endif()
if (NOT ZLIB_FOUND)
find_package(ZLIB)
@@ -16,6 +24,17 @@ if (ZLIB_FOUND)
endif()
endif()
+if (CRC32C_FOUND)
+ set(TD_HAVE_CRC32C 1)
+endif()
+
+if (TD_WITH_ABSEIL)
+ find_package(ABSL QUIET)
+ if (ABSL_FOUND)
+ set(TD_HAVE_ABSL 1)
+ endif()
+endif()
+
configure_file(td/utils/config.h.in td/utils/config.h @ONLY)
add_subdirectory(generate)
@@ -33,37 +52,55 @@ endif()
set(TDUTILS_SOURCE
td/utils/port/Clocks.cpp
- td/utils/port/Fd.cpp
td/utils/port/FileFd.cpp
td/utils/port/IPAddress.cpp
+ td/utils/port/MemoryMapping.cpp
td/utils/port/path.cpp
+ td/utils/port/platform.cpp
+ td/utils/port/PollFlags.cpp
+ td/utils/port/rlimit.cpp
td/utils/port/ServerSocketFd.cpp
td/utils/port/signals.cpp
td/utils/port/sleep.cpp
td/utils/port/SocketFd.cpp
+ td/utils/port/stacktrace.cpp
td/utils/port/Stat.cpp
+ td/utils/port/StdStreams.cpp
td/utils/port/thread_local.cpp
+ td/utils/port/UdpSocketFd.cpp
+ td/utils/port/uname.cpp
+ td/utils/port/user.cpp
td/utils/port/wstring_convert.cpp
td/utils/port/detail/Epoll.cpp
td/utils/port/detail/EventFdBsd.cpp
td/utils/port/detail/EventFdLinux.cpp
td/utils/port/detail/EventFdWindows.cpp
+ td/utils/port/detail/Iocp.cpp
td/utils/port/detail/KQueue.cpp
+ td/utils/port/detail/NativeFd.cpp
td/utils/port/detail/Poll.cpp
td/utils/port/detail/Select.cpp
td/utils/port/detail/ThreadIdGuard.cpp
+ td/utils/port/detail/ThreadPthread.cpp
td/utils/port/detail/WineventPoll.cpp
${TDMIME_AUTO}
+ td/utils/AsyncFileLog.cpp
td/utils/base64.cpp
td/utils/BigNum.cpp
td/utils/buffer.cpp
+ td/utils/BufferedUdp.cpp
+ td/utils/check.cpp
td/utils/crypto.cpp
+ td/utils/emoji.cpp
+ td/utils/ExitGuard.cpp
td/utils/FileLog.cpp
td/utils/filesystem.cpp
td/utils/find_boundary.cpp
+ td/utils/FlatHashTable.cpp
+ td/utils/FloodControlGlobal.cpp
td/utils/Gzip.cpp
td/utils/GzipByteFlow.cpp
td/utils/Hints.cpp
@@ -71,14 +108,24 @@ set(TDUTILS_SOURCE
td/utils/JsonBuilder.cpp
td/utils/logging.cpp
td/utils/misc.cpp
- td/utils/MimeType.cpp
+ td/utils/MpmcQueue.cpp
+ td/utils/OptionParser.cpp
+ td/utils/PathView.cpp
td/utils/Random.cpp
+ td/utils/SharedSlice.cpp
+ td/utils/Slice.cpp
td/utils/StackAllocator.cpp
td/utils/Status.cpp
td/utils/StringBuilder.cpp
+ td/utils/tests.cpp
td/utils/Time.cpp
td/utils/Timer.cpp
+ td/utils/TsFileLog.cpp
td/utils/tl_parsers.cpp
+ td/utils/translit.cpp
+ td/utils/TsCerr.cpp
+ td/utils/TsFileLog.cpp
+ td/utils/TsLog.cpp
td/utils/unicode.cpp
td/utils/utf8.cpp
@@ -87,57 +134,98 @@ set(TDUTILS_SOURCE
td/utils/port/CxCli.h
td/utils/port/EventFd.h
td/utils/port/EventFdBase.h
- td/utils/port/Fd.h
td/utils/port/FileFd.h
+ td/utils/port/FromApp.h
td/utils/port/IPAddress.h
+ td/utils/port/IoSlice.h
+ td/utils/port/MemoryMapping.h
+ td/utils/port/Mutex.h
td/utils/port/path.h
td/utils/port/platform.h
td/utils/port/Poll.h
td/utils/port/PollBase.h
+ td/utils/port/PollFlags.h
+ td/utils/port/rlimit.h
td/utils/port/RwMutex.h
td/utils/port/ServerSocketFd.h
td/utils/port/signals.h
td/utils/port/sleep.h
td/utils/port/SocketFd.h
+ td/utils/port/stacktrace.h
td/utils/port/Stat.h
+ td/utils/port/StdStreams.h
td/utils/port/thread.h
td/utils/port/thread_local.h
+ td/utils/port/UdpSocketFd.h
+ td/utils/port/uname.h
+ td/utils/port/user.h
td/utils/port/wstring_convert.h
td/utils/port/detail/Epoll.h
td/utils/port/detail/EventFdBsd.h
td/utils/port/detail/EventFdLinux.h
td/utils/port/detail/EventFdWindows.h
+ td/utils/port/detail/Iocp.h
td/utils/port/detail/KQueue.h
+ td/utils/port/detail/NativeFd.h
td/utils/port/detail/Poll.h
+ td/utils/port/detail/PollableFd.h
td/utils/port/detail/Select.h
+ td/utils/port/detail/skip_eintr.h
td/utils/port/detail/ThreadIdGuard.h
td/utils/port/detail/ThreadPthread.h
td/utils/port/detail/ThreadStl.h
td/utils/port/detail/WineventPoll.h
td/utils/AesCtrByteFlow.h
+ td/utils/algorithm.h
+ td/utils/as.h
+ td/utils/AsyncFileLog.h
+ td/utils/AtomicRead.h
td/utils/base64.h
td/utils/benchmark.h
td/utils/BigNum.h
+ td/utils/bits.h
td/utils/buffer.h
td/utils/BufferedFd.h
td/utils/BufferedReader.h
+ td/utils/BufferedUdp.h
td/utils/ByteFlow.h
+ td/utils/CancellationToken.h
+ td/utils/ChainScheduler.h
td/utils/ChangesProcessor.h
+ td/utils/check.h
td/utils/Closure.h
+ td/utils/CombinedLog.h
td/utils/common.h
+ td/utils/ConcurrentHashTable.h
td/utils/Container.h
+ td/utils/Context.h
td/utils/crypto.h
+ td/utils/DecTree.h
+ td/utils/Destructor.h
+ td/utils/emoji.h
td/utils/Enumerator.h
+ td/utils/EpochBasedMemoryReclamation.h
+ td/utils/ExitGuard.h
td/utils/FileLog.h
td/utils/filesystem.h
+ td/utils/fixed_vector.h
td/utils/find_boundary.h
+ td/utils/FlatHashMap.h
+ td/utils/FlatHashMapChunks.h
+ td/utils/FlatHashSet.h
+ td/utils/FlatHashTable.h
td/utils/FloodControlFast.h
+ td/utils/FloodControlGlobal.h
td/utils/FloodControlStrict.h
td/utils/format.h
td/utils/Gzip.h
td/utils/GzipByteFlow.h
+ td/utils/Hash.h
+ td/utils/HashMap.h
+ td/utils/HashSet.h
+ td/utils/HashTableUtils.h
td/utils/HazardPointers.h
td/utils/Heap.h
td/utils/Hints.h
@@ -147,8 +235,8 @@ set(TDUTILS_SOURCE
td/utils/JsonBuilder.h
td/utils/List.h
td/utils/logging.h
+ td/utils/MapNode.h
td/utils/MemoryLog.h
- td/utils/MimeType.h
td/utils/misc.h
td/utils/MovableValue.h
td/utils/MpmcQueue.h
@@ -156,68 +244,110 @@ set(TDUTILS_SOURCE
td/utils/MpscPollableQueue.h
td/utils/MpscLinkQueue.h
td/utils/Named.h
+ td/utils/NullLog.h
td/utils/ObjectPool.h
td/utils/Observer.h
td/utils/optional.h
- td/utils/OptionsParser.h
+ td/utils/OptionParser.h
td/utils/OrderedEventsProcessor.h
td/utils/overloaded.h
td/utils/Parser.h
td/utils/PathView.h
+ td/utils/Promise.h
td/utils/queue.h
td/utils/Random.h
td/utils/ScopeGuard.h
+ td/utils/SetNode.h
td/utils/SharedObjectPool.h
+ td/utils/SharedSlice.h
td/utils/Slice-decl.h
td/utils/Slice.h
+ td/utils/SliceBuilder.h
+ td/utils/Span.h
td/utils/SpinLock.h
td/utils/StackAllocator.h
td/utils/Status.h
+ td/utils/StealingQueue.h
td/utils/Storer.h
td/utils/StorerBase.h
td/utils/StringBuilder.h
td/utils/tests.h
+ td/utils/ThreadLocalStorage.h
+ td/utils/ThreadSafeCounter.h
td/utils/Time.h
td/utils/TimedStat.h
td/utils/Timer.h
td/utils/tl_helpers.h
td/utils/tl_parsers.h
td/utils/tl_storers.h
+ td/utils/TlDowncastHelper.h
+ td/utils/TlStorerToString.h
+ td/utils/translit.h
+ td/utils/TsCerr.h
+ td/utils/TsFileLog.h
+ td/utils/TsList.h
+ td/utils/TsLog.h
td/utils/type_traits.h
+ td/utils/UInt.h
+ td/utils/uint128.h
td/utils/unicode.h
+ td/utils/unique_ptr.h
td/utils/utf8.h
td/utils/Variant.h
+ td/utils/VectorQueue.h
+ td/utils/WaitFreeHashMap.h
+ td/utils/WaitFreeHashSet.h
+ td/utils/WaitFreeVector.h
)
+if (TDUTILS_MIME_TYPE)
+ set(TDUTILS_SOURCE
+ ${TDUTILS_SOURCE}
+ td/utils/MimeType.cpp
+ td/utils/MimeType.h
+ )
+endif()
+
set(TDUTILS_TEST_SOURCE
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/bitmask.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/buffer.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/ChainScheduler.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/ConcurrentHashMap.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/crypto.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/emoji.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/Enumerator.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/EpochBasedMemoryReclamation.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/filesystem.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/gzip.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/HazardPointers.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/HashSet.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/heap.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/HttpUrl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/json.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/List.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/log.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/misc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcQueue.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcWaiter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/MpscLinkQueue.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/OptionParser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/OrderedEventsProcessor.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/port.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/pq.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedObjectPool.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/SharedSlice.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/StealingQueue.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/variant.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/WaitFreeHashMap.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/WaitFreeHashSet.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test/WaitFreeVector.cpp
PARENT_SCOPE
)
-#RULES
#LIBRARIES
add_library(tdutils STATIC ${TDUTILS_SOURCE})
-if (WIN32)
- # find_library(WS2_32_LIBRARY ws2_32)
- # find_library(MSWSOCK_LIBRARY Mswsock)
- # target_link_libraries(tdutils PRIVATE ${WS2_32_LIBRARY} ${MSWSOCK_LIBRARY})
- target_link_libraries(tdutils PRIVATE ws2_32 Mswsock)
-endif()
-if (NOT CMAKE_CROSSCOMPILING)
+
+if (NOT CMAKE_CROSSCOMPILING AND TDUTILS_MIME_TYPE)
add_dependencies(tdutils tdmime_auto)
endif()
@@ -229,6 +359,14 @@ target_include_directories(tdutils PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOUR
if (OPENSSL_FOUND)
target_link_libraries(tdutils PRIVATE ${OPENSSL_CRYPTO_LIBRARY} ${CMAKE_DL_LIBS} ${ZLIB_LIBRARIES})
target_include_directories(tdutils SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR})
+
+ if (WIN32)
+ if (MINGW)
+ target_link_libraries(tdutils PRIVATE ws2_32 mswsock crypt32)
+ else()
+ target_link_libraries(tdutils PRIVATE ws2_32 Mswsock Crypt32)
+ endif()
+ endif()
endif()
if (ZLIB_FOUND)
@@ -236,9 +374,49 @@ if (ZLIB_FOUND)
target_include_directories(tdutils SYSTEM PRIVATE ${ZLIB_INCLUDE_DIR})
endif()
+if (CRC32C_FOUND)
+ target_link_libraries(tdutils PRIVATE crc32c)
+endif()
+if (ABSL_FOUND)
+ target_link_libraries(tdutils PUBLIC absl::flat_hash_map absl::flat_hash_set absl::hash)
+endif()
+
+if (WIN32)
+ # find_library for system libraries doesn't work for UWP builds
+ # find_library(WS2_32_LIBRARY ws2_32)
+ # find_library(MSWSOCK_LIBRARY Mswsock)
+ # target_link_libraries(tdutils PRIVATE ${WS2_32_LIBRARY} ${MSWSOCK_LIBRARY})
+ if (MINGW)
+ target_link_libraries(tdutils PRIVATE ws2_32 mswsock normaliz psapi)
+ else()
+ target_link_libraries(tdutils PRIVATE ws2_32 Mswsock Normaliz psapi)
+ endif()
+ if (NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
+ target_link_libraries(tdutils PRIVATE shell32)
+ endif()
+endif()
+
+if (ANDROID)
+ target_link_libraries(tdutils PRIVATE log)
+endif()
+
+if (CMAKE_SYSTEM_NAME MATCHES "NetBSD")
+ target_link_directories(tdutils PUBLIC /usr/pkg/gcc12/x86_64--netbsd/lib /usr/pkg/gcc12/i486--netbsdelf/lib)
+ target_link_libraries(tdutils PUBLIC atomic)
+endif()
+
install(TARGETS tdutils EXPORT TdTargets
- LIBRARY DESTINATION lib
- ARCHIVE DESTINATION lib
- RUNTIME DESTINATION bin
- INCLUDES DESTINATION include
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
)
+
+if (TD_TEST_FOLLY AND ABSL_FOUND)
+ find_package(benchmark QUIET)
+ find_package(folly QUIET)
+ find_package(gflags QUIET)
+
+ if (benchmark_FOUND AND folly_FOUND)
+ add_executable(benchmark-hashset test/hashset_benchmark.cpp)
+ target_link_libraries(benchmark-hashset PRIVATE tdutils benchmark::benchmark Folly::folly absl::flat_hash_map absl::hash)
+ endif()
+endif()
diff --git a/protocols/Telegram/tdlib/td/tdutils/generate/CMakeLists.txt b/protocols/Telegram/tdlib/td/tdutils/generate/CMakeLists.txt
index 69697b04b8..69b42d9144 100644
--- a/protocols/Telegram/tdlib/td/tdutils/generate/CMakeLists.txt
+++ b/protocols/Telegram/tdlib/td/tdutils/generate/CMakeLists.txt
@@ -1,7 +1,13 @@
-cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
+if ((CMAKE_MAJOR_VERSION LESS 3) OR (CMAKE_VERSION VERSION_LESS "3.0.2"))
+ message(FATAL_ERROR "CMake >= 3.0.2 is required")
+endif()
# Generates files for MIME type <-> extension conversions
-# DEPENDS ON: gperf grep bash/powershell
+# DEPENDS ON: gperf grep
+
+if (NOT TDUTILS_MIME_TYPE)
+ return()
+endif()
file(MAKE_DIRECTORY auto)
@@ -19,7 +25,7 @@ add_custom_target(tdmime_auto DEPENDS ${TDMIME_SOURCE})
if (NOT CMAKE_CROSSCOMPILING)
find_program(GPERF_EXECUTABLE gperf)
if (NOT GPERF_EXECUTABLE)
- message(FATAL_ERROR "Could NOT find gperf. Path to gperf needs to be specified manually, i.e. 'cmake -DGPERF_EXECUTABLE:FILEPATH=\"<path to gperf executable>\" .')")
+ message(FATAL_ERROR "Could NOT find gperf. Add path to gperf executable to PATH environment variable or specify it manually using GPERF_EXECUTABLE option, i.e. 'cmake -DGPERF_EXECUTABLE:FILEPATH=\"<path to gperf executable>\"'.")
endif()
set(GPERF_FILES
@@ -38,7 +44,7 @@ if (NOT CMAKE_CROSSCOMPILING)
DEPENDS generate_mime_types_gperf mime_types.txt
)
- if (WIN32)
+ if (CMAKE_HOST_WIN32)
set(MIME_TYPE_TO_EXTENSION_CMD ${GPERF_EXECUTABLE} -m100 --output-file=auto/mime_type_to_extension.cpp auto/mime_type_to_extension.gperf)
else()
set(MIME_TYPE_TO_EXTENSION_CMD ${GPERF_EXECUTABLE} -m100 auto/mime_type_to_extension.gperf | grep -v __gnu_inline__ > auto/mime_type_to_extension.cpp)
@@ -47,10 +53,10 @@ if (NOT CMAKE_CROSSCOMPILING)
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/auto/mime_type_to_extension.cpp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${MIME_TYPE_TO_EXTENSION_CMD}
- DEPENDS auto/mime_type_to_extension.gperf
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/auto/mime_type_to_extension.gperf
)
- if (WIN32)
+ if (CMAKE_HOST_WIN32)
set(EXTENSION_TO_MIME_TYPE_CMD ${GPERF_EXECUTABLE} -m100 --output-file=auto/extension_to_mime_type.cpp auto/extension_to_mime_type.gperf)
else()
set(EXTENSION_TO_MIME_TYPE_CMD ${GPERF_EXECUTABLE} -m100 auto/extension_to_mime_type.gperf | grep -v __gnu_inline__ > auto/extension_to_mime_type.cpp)
@@ -59,6 +65,6 @@ if (NOT CMAKE_CROSSCOMPILING)
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/auto/extension_to_mime_type.cpp
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND ${EXTENSION_TO_MIME_TYPE_CMD}
- DEPENDS auto/extension_to_mime_type.gperf
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/auto/extension_to_mime_type.gperf
)
endif()
diff --git a/protocols/Telegram/tdlib/td/tdutils/generate/generate_mime_types_gperf.cpp b/protocols/Telegram/tdlib/td/tdutils/generate/generate_mime_types_gperf.cpp
index ac7ff68605..44ab2bd1e9 100644
--- a/protocols/Telegram/tdlib/td/tdutils/generate/generate_mime_types_gperf.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/generate/generate_mime_types_gperf.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,7 +15,7 @@
#include <utility>
#include <vector>
-std::pair<std::string, std::string> split(std::string s, char delimiter = ' ') {
+static std::pair<std::string, std::string> split(std::string s, char delimiter = ' ') {
auto delimiter_pos = s.find(delimiter);
if (delimiter_pos == std::string::npos) {
return {std::move(s), ""};
@@ -26,8 +26,8 @@ std::pair<std::string, std::string> split(std::string s, char delimiter = ' ') {
}
}
-bool generate(const char *file_name, const char *from_name, const char *to_name,
- const std::map<std::string, std::string> &map) {
+static bool generate(const char *file_name, const char *from_name, const char *to_name,
+ const std::map<std::string, std::string> &map) {
// binary mode is needed for MSYS2 gperf
std::ofstream out(file_name, std::ios_base::trunc | std::ios_base::binary);
if (!out) {
@@ -71,6 +71,10 @@ bool generate(const char *file_name, const char *from_name, const char *to_name,
return true;
}
+static bool is_private_mime_type(const std::string &mime_type) {
+ return mime_type.find("/x-") != std::string::npos;
+}
+
int main(int argc, char *argv[]) {
if (argc != 4) {
std::cerr << "Wrong number of arguments supplied. Expected 'generate_mime_types_gperf <mime_types.txt> "
@@ -112,7 +116,7 @@ int main(int argc, char *argv[]) {
std::vector<std::string> extensions;
while (!extensions_string.empty()) {
- extensions.push_back("");
+ extensions.emplace_back();
std::tie(extensions.back(), extensions_string) = split(extensions_string);
}
assert(!extensions.empty());
@@ -132,7 +136,13 @@ int main(int argc, char *argv[]) {
for (auto &extension : extensions) {
if (!extension_to_mime_type.emplace(extension, mime_type).second) {
- std::cerr << "Extension \"" << extension << "\" matches more than one type" << std::endl;
+ if (is_private_mime_type(extension_to_mime_type[extension]) == is_private_mime_type(mime_type)) {
+ std::cerr << "Extension \"" << extension << "\" matches more than one type" << std::endl;
+ } else {
+ if (!is_private_mime_type(mime_type)) {
+ extension_to_mime_type[extension] = mime_type;
+ }
+ }
}
}
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/generate/mime_types.txt b/protocols/Telegram/tdlib/td/tdutils/generate/mime_types.txt
index a2d4abbf29..18dcfb775b 100644
--- a/protocols/Telegram/tdlib/td/tdutils/generate/mime_types.txt
+++ b/protocols/Telegram/tdlib/td/tdutils/generate/mime_types.txt
@@ -14,7 +14,7 @@ application/davmount+xml davmount
application/docbook+xml dbk
application/dssc+der dssc
application/dssc+xml xdssc
-application/ecmascript ecma
+application/ecmascript es
application/emma+xml emma
application/epub+zip epub
application/exi exi
@@ -498,6 +498,7 @@ application/x-dtbresource+xml res
application/x-dvi dvi
application/x-envoy evy
application/x-eva eva
+application/x-fictionbook+xml fb2
application/x-font-bdf bdf
application/x-font-ghostscript gsf
application/x-font-linux-psf psf
@@ -506,7 +507,7 @@ application/x-font-pcf pcf
application/x-font-snf snf
application/x-font-ttf ttf ttc
application/x-font-type1 pfa pfb pfm afm
-application/font-woff woff
+application/x-font-woff woff
application/x-freearc arc
application/x-futuresplash spl
application/x-gca-compressed gca
@@ -564,6 +565,8 @@ application/x-tex tex
application/x-tex-tfm tfm
application/x-texinfo texinfo texi
application/x-tgif obj
+application/x-tgsticker tgs
+application/x-tgwallpattern tgv
application/x-ustar ustar
application/x-wais-source src
application/x-x509-ca-cert der crt
@@ -589,9 +592,9 @@ application/zip zip
audio/adpcm adp
audio/basic au snd
audio/midi mid midi kar rmi
-audio/mp4 mp4a
+audio/mp4 m4a mp4a
audio/mpeg mpga mp2 mp2a mp3 m2a m3a
-audio/ogg oga ogg spx
+audio/ogg oga ogg opus spx
audio/s3m s3m
audio/silk sil
audio/vnd.dece.audio uva uvva
@@ -624,10 +627,19 @@ chemical/x-cmdf cmdf
chemical/x-cml cml
chemical/x-csml csml
chemical/x-xyz xyz
+font/collection ttc
+font/otf otf
+font/ttf ttf
+font/woff woff
+font/woff2 woff2
image/bmp bmp
image/cgm cgm
image/g3fax g3
image/gif gif
+image/heic heic
+image/heic-sequence heics
+image/heif heif
+image/heif-sequence heifs
image/ief ief
image/jpeg jpeg jpg jpe
image/ktx ktx
@@ -638,8 +650,8 @@ image/svg+xml svg svgz
image/tiff tiff tif
image/vnd.adobe.photoshop psd
image/vnd.dece.graphic uvi uvvi uvg uvvg
-image/vnd.dvb.subtitle sub
image/vnd.djvu djvu djv
+image/vnd.dvb.subtitle sub
image/vnd.dwg dwg
image/vnd.dxf dxf
image/vnd.fastbidsheet fbs
@@ -700,8 +712,8 @@ text/uri-list uri uris urls
text/vcard vcard
text/vnd.curl curl
text/vnd.curl.dcurl dcurl
-text/vnd.curl.scurl scurl
text/vnd.curl.mcurl mcurl
+text/vnd.curl.scurl scurl
text/vnd.dvb.subtitle sub
text/vnd.fly fly
text/vnd.fmi.flexstor flx
@@ -715,9 +727,10 @@ text/x-asm s asm
text/x-c c cc cxx cpp h hh dic
text/x-fortran f for f77 f90
text/x-java-source java
+text/x-nfo nfo
text/x-opml opml
text/x-pascal p pas
-text/x-nfo nfo
+text/x-php php
text/x-setext etx
text/x-sfv sfv
text/x-uuencode uu
@@ -728,6 +741,7 @@ video/3gpp2 3g2
video/h261 h261
video/h263 h263
video/h264 h264
+video/h265 h265
video/jpeg jpgv
video/jpm jpm jpgm
video/mj2 mj2 mjp2
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/AesCtrByteFlow.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/AesCtrByteFlow.h
index 820aa02fbe..5d9057f215 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/AesCtrByteFlow.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/AesCtrByteFlow.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,14 +11,15 @@
#include "td/utils/crypto.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
+#include "td/utils/UInt.h"
namespace td {
#if TD_HAVE_OPENSSL
-class AesCtrByteFlow : public ByteFlowInplaceBase {
+class AesCtrByteFlow final : public ByteFlowInplaceBase {
public:
void init(const UInt256 &key, const UInt128 &iv) {
- state_.init(key, iv);
+ state_.init(as_slice(key), as_slice(iv));
}
void init(AesCtrState &&state) {
state_ = std::move(state);
@@ -26,25 +27,20 @@ class AesCtrByteFlow : public ByteFlowInplaceBase {
AesCtrState move_aes_ctr_state() {
return std::move(state_);
}
- void loop() override {
- bool was_updated = false;
- while (true) {
- auto ready = input_->prepare_read();
- if (ready.empty()) {
- break;
- }
+ bool loop() final {
+ bool result = false;
+ auto ready = input_->prepare_read();
+ if (!ready.empty()) {
state_.encrypt(ready, MutableSlice(const_cast<char *>(ready.data()), ready.size()));
input_->confirm_read(ready.size());
output_.advance_end(ready.size());
- was_updated = true;
- }
- if (was_updated) {
- on_output_updated();
+ result = true;
}
+
if (!is_input_active_) {
finish(Status::OK()); // End of input stream.
}
- set_need_size(1);
+ return result;
}
private:
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.cpp
new file mode 100644
index 0000000000..575333745d
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.cpp
@@ -0,0 +1,160 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/AsyncFileLog.h"
+
+#include "td/utils/port/FileFd.h"
+#include "td/utils/port/path.h"
+#include "td/utils/port/sleep.h"
+#include "td/utils/port/StdStreams.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/Time.h"
+
+namespace td {
+
+#if !TD_THREAD_UNSUPPORTED
+
+Status AsyncFileLog::init(string path, int64 rotate_threshold, bool redirect_stderr) {
+ CHECK(path_.empty());
+ CHECK(!path.empty());
+
+ TRY_RESULT(fd, FileFd::open(path, FileFd::Create | FileFd::Write | FileFd::Append));
+ if (!Stderr().empty() && redirect_stderr) {
+ fd.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
+ }
+
+ auto r_path = realpath(path, true);
+ if (r_path.is_error()) {
+ path_ = std::move(path);
+ } else {
+ path_ = r_path.move_as_ok();
+ }
+ TRY_RESULT(size, fd.get_size());
+
+ queue_ = td::make_unique<MpscPollableQueue<Query>>();
+ queue_->init();
+
+ logging_thread_ = td::thread(
+ [queue = queue_.get(), fd = std::move(fd), path = path_, size, rotate_threshold, redirect_stderr]() mutable {
+ auto after_rotation = [&] {
+ fd.close();
+ auto r_fd = FileFd::open(path, FileFd::Create | FileFd::Write | FileFd::Append);
+ if (r_fd.is_error()) {
+ process_fatal_error(PSLICE() << r_fd.error() << " in " << __FILE__ << " at " << __LINE__ << '\n');
+ }
+ fd = r_fd.move_as_ok();
+ if (!Stderr().empty() && redirect_stderr) {
+ fd.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
+ }
+ size = 0;
+ };
+ auto append = [&](CSlice slice) {
+ if (size > rotate_threshold) {
+ auto status = rename(path, PSLICE() << path << ".old");
+ if (status.is_error()) {
+ process_fatal_error(PSLICE() << status << " in " << __FILE__ << " at " << __LINE__ << '\n');
+ }
+ after_rotation();
+ }
+ while (!slice.empty()) {
+ if (redirect_stderr) {
+ while (has_log_guard()) {
+ // spin
+ }
+ }
+ auto r_size = fd.write(slice);
+ if (r_size.is_error()) {
+ process_fatal_error(PSLICE() << r_size.error() << " in " << __FILE__ << " at " << __LINE__ << '\n');
+ }
+ auto written = r_size.ok();
+ size += static_cast<int64>(written);
+ slice.remove_prefix(written);
+ }
+ };
+
+ while (true) {
+ int ready_count = queue->reader_wait_nonblock();
+ if (ready_count == 0) {
+ queue->reader_get_event_fd().wait(1000);
+ continue;
+ }
+ bool need_close = false;
+ while (ready_count-- > 0) {
+ Query query = queue->reader_get_unsafe();
+ switch (query.type_) {
+ case Query::Type::Log:
+ append(query.data_);
+ break;
+ case Query::Type::AfterRotation:
+ after_rotation();
+ break;
+ case Query::Type::Close:
+ need_close = true;
+ break;
+ default:
+ process_fatal_error("Invalid query type in AsyncFileLog");
+ }
+ }
+ queue->reader_flush();
+
+ if (need_close) {
+ fd.close();
+ break;
+ }
+ }
+ });
+
+ return Status::OK();
+}
+
+AsyncFileLog::~AsyncFileLog() {
+ if (queue_ == nullptr) {
+ return;
+ }
+ Query query;
+ query.type_ = Query::Type::Close;
+ queue_->writer_put(std::move(query));
+ logging_thread_.join();
+}
+
+vector<string> AsyncFileLog::get_file_paths() {
+ vector<string> result;
+ if (!path_.empty()) {
+ result.push_back(path_);
+ result.push_back(PSTRING() << path_ << ".old");
+ }
+ return result;
+}
+
+void AsyncFileLog::after_rotation() {
+ Query query;
+ query.type_ = Query::Type::AfterRotation;
+ if (queue_ == nullptr) {
+ process_fatal_error("AsyncFileLog is not inited");
+ }
+ queue_->writer_put(std::move(query));
+}
+
+void AsyncFileLog::do_append(int log_level, CSlice slice) {
+ Query query;
+ query.data_ = slice.str();
+ if (queue_ == nullptr) {
+ process_fatal_error("AsyncFileLog is not inited");
+ }
+ queue_->writer_put(std::move(query));
+ if (log_level == VERBOSITY_NAME(FATAL)) {
+ // it is not thread-safe to join logging_thread_ there, so just wait for the log line to be printed
+ auto end_time = Time::now() + 1.0;
+ while (!queue_->is_empty() && Time::now() < end_time) {
+ usleep_for(1000);
+ }
+ usleep_for(5000); // allow some time for the log line to be actually printed
+ }
+}
+
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.h
new file mode 100644
index 0000000000..1b0ab73897
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/AsyncFileLog.h
@@ -0,0 +1,51 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/MpscPollableQueue.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+
+namespace td {
+
+#if !TD_THREAD_UNSUPPORTED
+
+class AsyncFileLog final : public LogInterface {
+ public:
+ AsyncFileLog() = default;
+ AsyncFileLog(const AsyncFileLog &) = delete;
+ AsyncFileLog &operator=(const AsyncFileLog &) = delete;
+ AsyncFileLog(AsyncFileLog &&) = delete;
+ AsyncFileLog &operator=(AsyncFileLog &&) = delete;
+ ~AsyncFileLog();
+
+ Status init(string path, int64 rotate_threshold, bool redirect_stderr = true);
+
+ private:
+ struct Query {
+ enum class Type : int32 { Log, AfterRotation, Close };
+ Type type_ = Type::Log;
+ string data_;
+ };
+
+ string path_;
+ unique_ptr<MpscPollableQueue<Query>> queue_;
+ thread logging_thread_;
+
+ vector<string> get_file_paths() final;
+
+ void after_rotation() final;
+
+ void do_append(int log_level, CSlice slice) final;
+};
+
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/AtomicRead.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/AtomicRead.h
new file mode 100644
index 0000000000..d30e960a8c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/AtomicRead.h
@@ -0,0 +1,89 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/port/sleep.h"
+#include "td/utils/type_traits.h"
+
+#include <atomic>
+#include <cstring>
+#include <memory>
+
+namespace td {
+
+template <class T>
+class AtomicRead {
+ public:
+ void read(T &dest) const {
+ uint32 counter = 0;
+ auto wait = [&] {
+ counter++;
+ const int wait_each_count = 4;
+ if (counter % wait_each_count == 0) {
+ usleep_for(1);
+ }
+ };
+
+ while (true) {
+ static_assert(TD_IS_TRIVIALLY_COPYABLE(T), "T must be trivially copyable");
+ auto version_before = version.load();
+ if (version_before % 2 == 0) {
+ std::memcpy(&dest, &value, sizeof(dest));
+ auto version_after = version.load();
+ if (version_before == version_after) {
+ break;
+ }
+ }
+ wait();
+ }
+ }
+
+ struct Write {
+ explicit Write(AtomicRead *read) {
+ read->do_lock();
+ ptr.reset(read);
+ }
+ struct Destructor {
+ void operator()(AtomicRead *read) const {
+ read->do_unlock();
+ }
+ };
+ T &operator*() {
+ return value();
+ }
+ T *operator->() {
+ return &value();
+ }
+ T &value() {
+ CHECK(ptr);
+ return ptr->value;
+ }
+
+ private:
+ std::unique_ptr<AtomicRead, Destructor> ptr;
+ };
+
+ Write lock() {
+ return Write(this);
+ }
+
+ private:
+ std::atomic<uint64> version{0};
+ T value;
+
+ void do_lock() {
+ bool is_locked = ++version % 2 == 1;
+ CHECK(is_locked);
+ }
+ void do_unlock() {
+ bool is_unlocked = ++version % 2 == 0;
+ CHECK(is_unlocked);
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.cpp
index f553661d49..e7a93d4399 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -12,10 +12,13 @@ char disable_linker_warning_about_empty_file_bignum_cpp TD_UNUSED;
#include "td/utils/logging.h"
#include "td/utils/misc.h"
+#include "td/utils/SliceBuilder.h"
#include <openssl/bn.h>
#include <openssl/crypto.h>
+#include <algorithm>
+
namespace td {
class BigNumContext::Impl {
@@ -37,9 +40,8 @@ class BigNumContext::Impl {
BigNumContext::BigNumContext() : impl_(make_unique<Impl>()) {
}
-BigNumContext::BigNumContext(BigNumContext &&other) = default;
-BigNumContext &BigNumContext::operator=(BigNumContext &&other) = default;
-
+BigNumContext::BigNumContext(BigNumContext &&other) noexcept = default;
+BigNumContext &BigNumContext::operator=(BigNumContext &&other) noexcept = default;
BigNumContext::~BigNumContext() = default;
class BigNum::Impl {
@@ -68,6 +70,9 @@ BigNum::BigNum(const BigNum &other) : BigNum() {
}
BigNum &BigNum::operator=(const BigNum &other) {
+ if (this == &other) {
+ return *this;
+ }
CHECK(impl_ != nullptr);
CHECK(other.impl_ != nullptr);
BIGNUM *result = BN_copy(impl_->big_num, other.impl_->big_num);
@@ -75,20 +80,41 @@ BigNum &BigNum::operator=(const BigNum &other) {
return *this;
}
-BigNum::BigNum(BigNum &&other) = default;
-
-BigNum &BigNum::operator=(BigNum &&other) = default;
-
+BigNum::BigNum(BigNum &&other) noexcept = default;
+BigNum &BigNum::operator=(BigNum &&other) noexcept = default;
BigNum::~BigNum() = default;
BigNum BigNum::from_binary(Slice str) {
return BigNum(make_unique<Impl>(BN_bin2bn(str.ubegin(), narrow_cast<int>(str.size()), nullptr)));
}
-BigNum BigNum::from_decimal(CSlice str) {
+BigNum BigNum::from_le_binary(Slice str) {
+#if defined(OPENSSL_IS_BORINGSSL)
+ return BigNum(make_unique<Impl>(BN_le2bn(str.ubegin(), narrow_cast<int>(str.size()), nullptr)));
+#elif OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+ return BigNum(make_unique<Impl>(BN_lebin2bn(str.ubegin(), narrow_cast<int>(str.size()), nullptr)));
+#else
+ string str_copy = str.str();
+ std::reverse(str_copy.begin(), str_copy.end());
+ return from_binary(str_copy);
+#endif
+}
+
+Result<BigNum> BigNum::from_decimal(CSlice str) {
BigNum result;
- int err = BN_dec2bn(&result.impl_->big_num, str.c_str());
- LOG_IF(FATAL, err == 0);
+ int res = BN_dec2bn(&result.impl_->big_num, str.c_str());
+ if (res == 0 || static_cast<size_t>(res) != str.size()) {
+ return Status::Error(PSLICE() << "Failed to parse \"" << str << "\" as BigNum");
+ }
+ return result;
+}
+
+Result<BigNum> BigNum::from_hex(CSlice str) {
+ BigNum result;
+ int res = BN_hex2bn(&result.impl_->big_num, str.c_str());
+ if (res == 0 || static_cast<size_t>(res) != str.size()) {
+ return Status::Error(PSLICE() << "Failed to parse \"" << str << "\" as hexadecimal BigNum");
+ }
return result;
}
@@ -99,10 +125,6 @@ BigNum BigNum::from_raw(void *openssl_big_num) {
BigNum::BigNum(unique_ptr<Impl> &&impl) : impl_(std::move(impl)) {
}
-void BigNum::ensure_const_time() {
- BN_set_flags(impl_->big_num, BN_FLG_CONSTTIME);
-}
-
int BigNum::get_num_bits() const {
return BN_num_bits(impl_->big_num);
}
@@ -126,7 +148,12 @@ bool BigNum::is_bit_set(int num) const {
}
bool BigNum::is_prime(BigNumContext &context) const {
- int result = BN_is_prime_ex(impl_->big_num, BN_prime_checks, context.impl_->big_num_context, nullptr);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ int result = BN_check_prime(impl_->big_num, context.impl_->big_num_context, nullptr);
+#else
+ int result =
+ BN_is_prime_ex(impl_->big_num, get_num_bits() > 2048 ? 128 : 64, context.impl_->big_num_context, nullptr);
+#endif
LOG_IF(FATAL, result == -1);
return result == 1;
}
@@ -180,10 +207,32 @@ string BigNum::to_binary(int exact_size) const {
CHECK(exact_size >= num_size);
}
string res(exact_size, '\0');
- BN_bn2bin(impl_->big_num, reinterpret_cast<unsigned char *>(&res[exact_size - num_size]));
+ BN_bn2bin(impl_->big_num, MutableSlice(res).ubegin() + (exact_size - num_size));
return res;
}
+string BigNum::to_le_binary(int exact_size) const {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) || defined(OPENSSL_IS_BORINGSSL)
+ int num_size = get_num_bytes();
+ if (exact_size == -1) {
+ exact_size = num_size;
+ } else {
+ CHECK(exact_size >= num_size);
+ }
+ string result(exact_size, '\0');
+#if defined(OPENSSL_IS_BORINGSSL)
+ BN_bn2le_padded(MutableSlice(result).ubegin(), exact_size, impl_->big_num);
+#else
+ BN_bn2lebinpad(impl_->big_num, MutableSlice(result).ubegin(), exact_size);
+#endif
+ return result;
+#else
+ string result = to_binary(exact_size);
+ std::reverse(result.begin(), result.end());
+ return result;
+#endif
+}
+
string BigNum::to_decimal() const {
char *result = BN_bn2dec(impl_->big_num);
CHECK(result != nullptr);
@@ -214,12 +263,29 @@ void BigNum::mul(BigNum &r, BigNum &a, BigNum &b, BigNumContext &context) {
LOG_IF(FATAL, result != 1);
}
+void BigNum::mod_add(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context) {
+ int result = BN_mod_add(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, m.impl_->big_num,
+ context.impl_->big_num_context);
+ LOG_IF(FATAL, result != 1);
+}
+
+void BigNum::mod_sub(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context) {
+ int result = BN_mod_sub(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, m.impl_->big_num,
+ context.impl_->big_num_context);
+ LOG_IF(FATAL, result != 1);
+}
+
void BigNum::mod_mul(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context) {
int result = BN_mod_mul(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, m.impl_->big_num,
context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
+void BigNum::mod_inverse(BigNum &r, BigNum &a, const BigNum &m, BigNumContext &context) {
+ auto result = BN_mod_inverse(r.impl_->big_num, a.impl_->big_num, m.impl_->big_num, context.impl_->big_num_context);
+ LOG_IF(FATAL, result != r.impl_->big_num);
+}
+
void BigNum::div(BigNum *quotient, BigNum *remainder, const BigNum &dividend, const BigNum &divisor,
BigNumContext &context) {
auto q = quotient == nullptr ? nullptr : quotient->impl_->big_num;
@@ -247,5 +313,9 @@ int BigNum::compare(const BigNum &a, const BigNum &b) {
return BN_cmp(a.impl_->big_num, b.impl_->big_num);
}
+StringBuilder &operator<<(StringBuilder &sb, const BigNum &bn) {
+ return sb << bn.to_decimal();
+}
+
} // namespace td
#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.h
index 6eecdeab03..9b666f4ce0 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/BigNum.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,6 +11,8 @@
#if TD_HAVE_OPENSSL
#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+#include "td/utils/StringBuilder.h"
namespace td {
@@ -19,8 +21,8 @@ class BigNumContext {
BigNumContext();
BigNumContext(const BigNumContext &other) = delete;
BigNumContext &operator=(const BigNumContext &other) = delete;
- BigNumContext(BigNumContext &&other);
- BigNumContext &operator=(BigNumContext &&other);
+ BigNumContext(BigNumContext &&other) noexcept;
+ BigNumContext &operator=(BigNumContext &&other) noexcept;
~BigNumContext();
private:
@@ -35,20 +37,22 @@ class BigNum {
BigNum();
BigNum(const BigNum &other);
BigNum &operator=(const BigNum &other);
- BigNum(BigNum &&other);
- BigNum &operator=(BigNum &&other);
+ BigNum(BigNum &&other) noexcept;
+ BigNum &operator=(BigNum &&other) noexcept;
~BigNum();
static BigNum from_binary(Slice str);
- static BigNum from_decimal(CSlice str);
+ static BigNum from_le_binary(Slice str);
+
+ static Result<BigNum> from_decimal(CSlice str);
+
+ static Result<BigNum> from_hex(CSlice str);
static BigNum from_raw(void *openssl_big_num);
void set_value(uint32 new_value);
- void ensure_const_time();
-
int get_num_bits() const;
int get_num_bytes() const;
@@ -65,6 +69,8 @@ class BigNum {
string to_binary(int exact_size = -1) const;
+ string to_le_binary(int exact_size = -1) const;
+
string to_decimal() const;
void operator+=(uint32 value);
@@ -85,8 +91,14 @@ class BigNum {
static void mul(BigNum &r, BigNum &a, BigNum &b, BigNumContext &context);
+ static void mod_add(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context);
+
+ static void mod_sub(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context);
+
static void mod_mul(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context);
+ static void mod_inverse(BigNum &r, BigNum &a, const BigNum &m, BigNumContext &context);
+
static void div(BigNum *quotient, BigNum *remainder, const BigNum &dividend, const BigNum &divisor,
BigNumContext &context);
@@ -103,6 +115,8 @@ class BigNum {
explicit BigNum(unique_ptr<Impl> &&impl);
};
+StringBuilder &operator<<(StringBuilder &sb, const BigNum &bn);
+
} // namespace td
#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedFd.h
index 0c8f65408d..6bb9b77098 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedFd.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedFd.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,10 +7,13 @@
#pragma once
#include "td/utils/buffer.h"
+#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/IoSlice.h"
#include "td/utils/Slice.h"
+#include "td/utils/Span.h"
#include "td/utils/Status.h"
#include <limits>
@@ -28,15 +31,16 @@ class BufferedFdBase : public FdT {
Result<size_t> flush_write() TD_WARN_UNUSED_RESULT;
bool need_flush_write(size_t at_least = 0) {
- CHECK(write_);
- write_->sync_with_writer();
- return write_->size() > at_least;
+ return ready_for_flush_write() > at_least;
}
size_t ready_for_flush_write() {
CHECK(write_);
write_->sync_with_writer();
return write_->size();
}
+ void sync_with_poll() {
+ ::td::sync_with_poll(*this);
+ }
void set_input_writer(ChainBufferWriter *read) {
read_ = read;
}
@@ -50,7 +54,7 @@ class BufferedFdBase : public FdT {
};
template <class FdT>
-class BufferedFd : public BufferedFdBase<FdT> {
+class BufferedFd final : public BufferedFdBase<FdT> {
using Parent = BufferedFdBase<FdT>;
ChainBufferWriter input_writer_;
ChainBufferReader input_reader_;
@@ -62,14 +66,21 @@ class BufferedFd : public BufferedFdBase<FdT> {
public:
BufferedFd();
explicit BufferedFd(FdT &&fd_);
- BufferedFd(BufferedFd &&);
- BufferedFd &operator=(BufferedFd &&);
+ BufferedFd(BufferedFd &&) noexcept;
+ BufferedFd &operator=(BufferedFd &&) noexcept;
BufferedFd(const BufferedFd &) = delete;
BufferedFd &operator=(const BufferedFd &) = delete;
~BufferedFd();
void close();
+ size_t left_unread() const {
+ return input_reader_.size();
+ }
+ size_t left_unwritten() const {
+ return output_reader_.size();
+ }
+
Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT;
Result<size_t> flush_write() TD_WARN_UNUSED_RESULT;
@@ -89,8 +100,9 @@ template <class FdT>
Result<size_t> BufferedFdBase<FdT>::flush_read(size_t max_read) {
CHECK(read_);
size_t result = 0;
- while (::td::can_read(*this) && max_read) {
- MutableSlice slice = read_->prepare_append().truncate(max_read);
+ while (::td::can_read_local(*this) && max_read) {
+ MutableSlice slice = read_->prepare_append();
+ slice.truncate(max_read);
TRY_RESULT(x, FdT::read(slice));
slice.truncate(x);
read_->confirm_append(x);
@@ -102,13 +114,25 @@ Result<size_t> BufferedFdBase<FdT>::flush_read(size_t max_read) {
template <class FdT>
Result<size_t> BufferedFdBase<FdT>::flush_write() {
- size_t result = 0;
// TODO: sync on demand
write_->sync_with_writer();
- while (!write_->empty() && ::td::can_write(*this)) {
- Slice slice = write_->prepare_read();
- TRY_RESULT(x, FdT::write(slice));
- write_->confirm_read(x);
+ size_t result = 0;
+ while (!write_->empty() && ::td::can_write_local(*this)) {
+ constexpr size_t BUF_SIZE = 20;
+ IoSlice buf[BUF_SIZE];
+
+ auto it = write_->clone();
+ size_t buf_i;
+ for (buf_i = 0; buf_i < BUF_SIZE; buf_i++) {
+ Slice slice = it.prepare_read();
+ if (slice.empty()) {
+ break;
+ }
+ buf[buf_i] = as_io_slice(slice);
+ it.confirm_read(slice.size());
+ }
+ TRY_RESULT(x, FdT::writev(Span<IoSlice>(buf, buf_i)));
+ write_->advance(x);
result += x;
}
return result;
@@ -139,12 +163,12 @@ BufferedFd<FdT>::BufferedFd(FdT &&fd_) : Parent(std::move(fd_)) {
}
template <class FdT>
-BufferedFd<FdT>::BufferedFd(BufferedFd &&from) {
+BufferedFd<FdT>::BufferedFd(BufferedFd &&from) noexcept {
*this = std::move(from);
}
template <class FdT>
-BufferedFd<FdT> &BufferedFd<FdT>::operator=(BufferedFd &&from) {
+BufferedFd<FdT> &BufferedFd<FdT>::operator=(BufferedFd &&from) noexcept {
FdT::operator=(std::move(static_cast<FdT &>(from)));
input_reader_ = std::move(from.input_reader_);
input_writer_ = std::move(from.input_writer_);
@@ -171,7 +195,7 @@ Result<size_t> BufferedFd<FdT>::flush_read(size_t max_read) {
if (result) {
// TODO: faster sync is possible if you owns writer.
input_reader_.sync_with_writer();
- LOG(DEBUG) << "flush_read: +" << format::as_size(result) << tag("total", format::as_size(input_reader_.size()));
+ LOG(DEBUG) << "Flush read: +" << format::as_size(result) << tag("total", format::as_size(input_reader_.size()));
}
return result;
}
@@ -180,7 +204,7 @@ template <class FdT>
Result<size_t> BufferedFd<FdT>::flush_write() {
TRY_RESULT(result, Parent::flush_write());
if (result) {
- LOG(DEBUG) << "flush_write: +" << format::as_size(result) << tag("left", format::as_size(output_reader_.size()));
+ LOG(DEBUG) << "Flush write: +" << format::as_size(result) << tag("left", format::as_size(output_reader_.size()));
}
return result;
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedReader.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedReader.h
index 9006d78132..5fd8ac44fe 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedReader.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedReader.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,12 +11,11 @@
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
-#include <cstring>
-
namespace td {
+
class BufferedReader {
public:
- explciit BufferedReader(FileFd &file, size_t buff_size = 8152)
+ explicit BufferedReader(FileFd &file, size_t buff_size = 8152)
: file_(file), buff_(buff_size), begin_pos_(0), end_pos_(0) {
}
@@ -33,13 +32,13 @@ inline Result<size_t> BufferedReader::read(MutableSlice slice) {
size_t available = end_pos_ - begin_pos_;
if (available >= slice.size()) {
// have enough data in buffer
- std::memcpy(slice.begin(), &buff_[begin_pos_], slice.size());
+ slice.copy_from({&buff_[begin_pos_], slice.size()});
begin_pos_ += slice.size();
return slice.size();
}
if (available) {
- std::memcpy(slice.begin(), &buff_[begin_pos_], available);
+ slice.copy_from({&buff_[begin_pos_], available});
begin_pos_ += available;
slice.remove_prefix(available);
}
@@ -54,8 +53,9 @@ inline Result<size_t> BufferedReader::read(MutableSlice slice) {
end_pos_ = result;
size_t left = min(end_pos_, slice.size());
- std::memcpy(slice.begin(), &buff_[begin_pos_], left);
- begin_pos_ += left;
+ slice.copy_from({&buff_[0], left});
+ begin_pos_ = left;
return left + available;
}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.cpp
new file mode 100644
index 0000000000..e28bb97069
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.cpp
@@ -0,0 +1,17 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/BufferedUdp.h"
+
+char disable_linker_warning_about_empty_file_buffered_udp_cpp TD_UNUSED;
+
+namespace td {
+
+#if TD_PORT_POSIX
+TD_THREAD_LOCAL detail::UdpReader *BufferedUdp::udp_reader_;
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.h
new file mode 100644
index 0000000000..5907e17557
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/BufferedUdp.h
@@ -0,0 +1,177 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/buffer.h"
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/optional.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/thread_local.h"
+#include "td/utils/port/UdpSocketFd.h"
+#include "td/utils/Span.h"
+#include "td/utils/Status.h"
+#include "td/utils/VectorQueue.h"
+
+#include <array>
+
+namespace td {
+
+#if TD_PORT_POSIX
+namespace detail {
+class UdpWriter {
+ public:
+ static Status write_once(UdpSocketFd &fd, VectorQueue<UdpMessage> &queue) TD_WARN_UNUSED_RESULT {
+ std::array<UdpSocketFd::OutboundMessage, 16> messages;
+ auto to_send = queue.as_span();
+ size_t to_send_n = td::min(messages.size(), to_send.size());
+ to_send.truncate(to_send_n);
+ for (size_t i = 0; i < to_send_n; i++) {
+ messages[i].to = &to_send[i].address;
+ messages[i].data = to_send[i].data.as_slice();
+ }
+
+ size_t cnt;
+ auto status = fd.send_messages(Span<UdpSocketFd::OutboundMessage>(messages).truncate(to_send_n), cnt);
+ queue.pop_n(cnt);
+ return status;
+ }
+};
+
+class UdpReaderHelper {
+ public:
+ void init_inbound_message(UdpSocketFd::InboundMessage &message) {
+ message.from = &message_.address;
+ message.error = &message_.error;
+ if (buffer_.size() < MAX_PACKET_SIZE) {
+ buffer_ = BufferSlice(RESERVED_SIZE);
+ }
+ CHECK(buffer_.size() >= MAX_PACKET_SIZE);
+ message.data = buffer_.as_slice().substr(0, MAX_PACKET_SIZE);
+ }
+
+ UdpMessage extract_udp_message(UdpSocketFd::InboundMessage &message) {
+ message_.data = buffer_.from_slice(message.data);
+ auto size = message_.data.size();
+ size = (size + 7) & ~7;
+ CHECK(size <= MAX_PACKET_SIZE);
+ buffer_.confirm_read(size);
+ return std::move(message_);
+ }
+
+ private:
+ static constexpr size_t MAX_PACKET_SIZE = 2048;
+ static constexpr size_t RESERVED_SIZE = MAX_PACKET_SIZE * 8;
+ UdpMessage message_;
+ BufferSlice buffer_;
+};
+
+// One for thread is enough
+class UdpReader {
+ public:
+ UdpReader() {
+ for (size_t i = 0; i < messages_.size(); i++) {
+ helpers_[i].init_inbound_message(messages_[i]);
+ }
+ }
+ Status read_once(UdpSocketFd &fd, VectorQueue<UdpMessage> &queue) TD_WARN_UNUSED_RESULT {
+ for (auto &message : messages_) {
+ CHECK(message.data.size() == 2048);
+ }
+ size_t cnt = 0;
+ auto status = fd.receive_messages(messages_, cnt);
+ for (size_t i = 0; i < cnt; i++) {
+ queue.push(helpers_[i].extract_udp_message(messages_[i]));
+ helpers_[i].init_inbound_message(messages_[i]);
+ }
+ for (size_t i = cnt; i < messages_.size(); i++) {
+ LOG_CHECK(messages_[i].data.size() == 2048)
+ << " cnt = " << cnt << " i = " << i << " size = " << messages_[i].data.size() << " status = " << status;
+ }
+ if (status.is_error() && !UdpSocketFd::is_critical_read_error(status)) {
+ queue.push(UdpMessage{{}, {}, std::move(status)});
+ status = Status::OK();
+ }
+ return status;
+ }
+
+ private:
+ static constexpr size_t BUFFER_SIZE = 16;
+ std::array<UdpSocketFd::InboundMessage, BUFFER_SIZE> messages_;
+ std::array<UdpReaderHelper, BUFFER_SIZE> helpers_;
+};
+
+} // namespace detail
+
+#endif
+
+class BufferedUdp final : public UdpSocketFd {
+ public:
+ explicit BufferedUdp(UdpSocketFd fd) : UdpSocketFd(std::move(fd)) {
+ }
+
+#if TD_PORT_POSIX
+ void sync_with_poll() {
+ ::td::sync_with_poll(*this);
+ }
+ Result<optional<UdpMessage>> receive() {
+ if (input_.empty() && can_read_local(*this)) {
+ TRY_STATUS(flush_read_once());
+ }
+ if (input_.empty()) {
+ return optional<UdpMessage>();
+ }
+ return input_.pop();
+ }
+
+ void send(UdpMessage message) {
+ output_.push(std::move(message));
+ }
+
+ Status flush_send() {
+ Status status;
+ while (status.is_ok() && can_write_local(*this) && !output_.empty()) {
+ status = flush_send_once();
+ }
+ return status;
+ }
+#endif
+
+ UdpSocketFd move_as_udp_socket_fd() {
+ return std::move(as_fd());
+ }
+
+ UdpSocketFd &as_fd() {
+ return *static_cast<UdpSocketFd *>(this);
+ }
+
+ private:
+#if TD_PORT_POSIX
+ VectorQueue<UdpMessage> input_;
+ VectorQueue<UdpMessage> output_;
+
+ VectorQueue<UdpMessage> &input() {
+ return input_;
+ }
+ VectorQueue<UdpMessage> &output() {
+ return output_;
+ }
+
+ Status flush_send_once() TD_WARN_UNUSED_RESULT {
+ return detail::UdpWriter::write_once(as_fd(), output_);
+ }
+
+ Status flush_read_once() TD_WARN_UNUSED_RESULT {
+ init_thread_local<detail::UdpReader>(udp_reader_);
+ return udp_reader_->read_once(as_fd(), input_);
+ }
+
+ static TD_THREAD_LOCAL detail::UdpReader *udp_reader_;
+#endif
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ByteFlow.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ByteFlow.h
index fb0c4489eb..2043864656 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/ByteFlow.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ByteFlow.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,9 +8,10 @@
#include "td/utils/buffer.h"
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include "td/utils/Status.h"
+#include <limits>
+
namespace td {
class ByteFlowInterface {
@@ -20,6 +21,10 @@ class ByteFlowInterface {
virtual void set_parent(ByteFlowInterface &other) = 0;
virtual void set_input(ChainBufferReader *input) = 0;
virtual size_t get_need_size() = 0;
+ virtual size_t get_read_size() = 0;
+ virtual size_t get_write_size() = 0;
+ virtual void reset_need_size() {
+ }
ByteFlowInterface() = default;
ByteFlowInterface(const ByteFlowInterface &) = delete;
ByteFlowInterface &operator=(const ByteFlowInterface &) = delete;
@@ -42,36 +47,98 @@ class ByteFlowBaseCommon : public ByteFlowInterface {
}
void wakeup() final {
- if (stop_flag_) {
+ if (stop_flag_ || !input_) {
return;
}
input_->sync_with_writer();
+
if (waiting_flag_) {
if (!is_input_active_) {
finish(Status::OK());
}
return;
}
- if (is_input_active_) {
- if (need_size_ != 0 && input_->size() < need_size_) {
- return;
+ while (true) {
+ if (stop_flag_) {
+ break;
+ }
+
+ // update can_read
+ if (is_input_active_) {
+ auto read_size = get_read_size();
+ if (read_size < min(need_size_, options_.read_watermark.low)) {
+ can_read = false;
+ }
+ if (read_size >= max(need_size_, options_.read_watermark.high)) {
+ can_read = true;
+ }
+ } else {
+ // always can read when input is closed
+ can_read = true;
+ }
+
+ // update can_write
+ {
+ auto write_size = get_write_size();
+ if (write_size > options_.write_watermark.high) {
+ can_write = false;
+ }
+ if (write_size <= options_.write_watermark.low) {
+ can_write = true;
+ }
+ }
+
+ if (!can_read || !can_write) {
+ break;
+ }
+ need_size_ = 0;
+
+ if (!loop()) {
+ if (need_size_ <= get_read_size()) {
+ need_size_ = get_read_size() + 1;
+ }
}
}
- need_size_ = 0;
- loop();
+ on_output_updated();
}
size_t get_need_size() final {
return need_size_;
}
+ void reset_need_size() override {
+ need_size_ = 0;
+ }
+ size_t get_read_size() override {
+ input_->sync_with_writer();
+ return input_->size();
+ }
+ size_t get_write_size() override {
+ CHECK(parent_);
+ return parent_->get_read_size();
+ }
+
+ struct Watermark {
+ size_t low{std::numeric_limits<size_t>::max()};
+ size_t high{0};
+ };
+ struct Options {
+ Watermark write_watermark;
+ Watermark read_watermark;
+ };
+ void set_options(Options options) {
+ options_ = options;
+ }
- virtual void loop() = 0;
+ virtual bool loop() = 0;
protected:
bool waiting_flag_ = false;
- ChainBufferReader *input_;
+ ChainBufferReader *input_ = nullptr;
bool is_input_active_ = true;
size_t need_size_ = 0;
+ bool can_read{true};
+ bool can_write{true};
+ Options options_;
void finish(Status status) {
stop_flag_ = true;
need_size_ = 0;
@@ -115,7 +182,7 @@ class ByteFlowBase : public ByteFlowBaseCommon {
parent_ = &other;
parent_->set_input(&output_reader_);
}
- void loop() override = 0;
+ bool loop() override = 0;
// ChainBufferWriter &get_output() {
// return output_;
@@ -138,7 +205,7 @@ class ByteFlowInplaceBase : public ByteFlowBaseCommon {
parent_ = &other;
parent_->set_input(&output_);
}
- void loop() override = 0;
+ bool loop() override = 0;
ChainBufferReader &get_output() {
return output_;
@@ -153,16 +220,16 @@ inline ByteFlowInterface &operator>>(ByteFlowInterface &from, ByteFlowInterface
return to;
}
-class ByteFlowSource : public ByteFlowInterface {
+class ByteFlowSource final : public ByteFlowInterface {
public:
ByteFlowSource() = default;
explicit ByteFlowSource(ChainBufferReader *buffer) : buffer_(buffer) {
}
- ByteFlowSource(ByteFlowSource &&other) : buffer_(other.buffer_), parent_(other.parent_) {
+ ByteFlowSource(ByteFlowSource &&other) noexcept : buffer_(other.buffer_), parent_(other.parent_) {
other.buffer_ = nullptr;
other.parent_ = nullptr;
}
- ByteFlowSource &operator=(ByteFlowSource &&other) {
+ ByteFlowSource &operator=(ByteFlowSource &&other) noexcept {
buffer_ = other.buffer_;
parent_ = other.parent_;
other.buffer_ = nullptr;
@@ -187,7 +254,9 @@ class ByteFlowSource : public ByteFlowInterface {
parent_ = nullptr;
}
void wakeup() final {
- CHECK(parent_);
+ if (!parent_) {
+ return;
+ }
parent_->wakeup();
}
size_t get_need_size() final {
@@ -196,19 +265,27 @@ class ByteFlowSource : public ByteFlowInterface {
}
return parent_->get_need_size();
}
+ size_t get_read_size() final {
+ UNREACHABLE();
+ return 0;
+ }
+ size_t get_write_size() final {
+ CHECK(parent_);
+ return parent_->get_read_size();
+ }
private:
ChainBufferReader *buffer_ = nullptr;
ByteFlowInterface *parent_ = nullptr;
};
-class ByteFlowSink : public ByteFlowInterface {
+class ByteFlowSink final : public ByteFlowInterface {
public:
void set_input(ChainBufferReader *input) final {
CHECK(buffer_ == nullptr);
buffer_ = input;
}
- void set_parent(ByteFlowInterface &parent) final {
+ void set_parent(ByteFlowInterface & /*parent*/) final {
UNREACHABLE();
}
void close_input(Status status) final {
@@ -224,6 +301,14 @@ class ByteFlowSink : public ByteFlowInterface {
UNREACHABLE();
return 0;
}
+ size_t get_read_size() final {
+ buffer_->sync_with_writer();
+ return buffer_->size();
+ }
+ size_t get_write_size() final {
+ UNREACHABLE();
+ return 0;
+ }
bool is_ready() {
return !active_;
}
@@ -244,13 +329,17 @@ class ByteFlowSink : public ByteFlowInterface {
ChainBufferReader *buffer_ = nullptr;
};
-class ByteFlowMoveSink : public ByteFlowInterface {
+class ByteFlowMoveSink final : public ByteFlowInterface {
public:
+ ByteFlowMoveSink() = default;
+ explicit ByteFlowMoveSink(ChainBufferWriter *output) {
+ set_output(output);
+ }
void set_input(ChainBufferReader *input) final {
CHECK(!input_);
input_ = input;
}
- void set_parent(ByteFlowInterface &parent) final {
+ void set_parent(ByteFlowInterface & /*parent*/) final {
UNREACHABLE();
}
void close_input(Status status) final {
@@ -267,6 +356,15 @@ class ByteFlowMoveSink : public ByteFlowInterface {
UNREACHABLE();
return 0;
}
+ size_t get_read_size() final {
+ input_->sync_with_writer();
+ //TODO: must be input_->size() + output_->size()
+ return input_->size();
+ }
+ size_t get_write_size() final {
+ UNREACHABLE();
+ return 0;
+ }
void set_output(ChainBufferWriter *output) {
CHECK(!output_);
output_ = output;
@@ -285,4 +383,5 @@ class ByteFlowMoveSink : public ByteFlowInterface {
ChainBufferReader *input_ = nullptr;
ChainBufferWriter *output_ = nullptr;
};
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/CancellationToken.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/CancellationToken.h
new file mode 100644
index 0000000000..19f280655c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/CancellationToken.h
@@ -0,0 +1,71 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include <atomic>
+#include <memory>
+
+namespace td {
+
+namespace detail {
+struct RawCancellationToken {
+ std::atomic<bool> is_canceled_{false};
+};
+} // namespace detail
+
+class CancellationToken {
+ public:
+ explicit operator bool() const noexcept {
+ // empty CancellationToken is never canceled
+ if (!token_) {
+ return false;
+ }
+ return token_->is_canceled_.load(std::memory_order_acquire);
+ }
+ CancellationToken() = default;
+ explicit CancellationToken(std::shared_ptr<detail::RawCancellationToken> token) : token_(std::move(token)) {
+ }
+
+ private:
+ std::shared_ptr<detail::RawCancellationToken> token_;
+};
+
+class CancellationTokenSource {
+ public:
+ CancellationTokenSource() = default;
+ CancellationTokenSource(CancellationTokenSource &&other) noexcept : token_(std::move(other.token_)) {
+ }
+ CancellationTokenSource &operator=(CancellationTokenSource &&other) noexcept {
+ cancel();
+ token_ = std::move(other.token_);
+ return *this;
+ }
+ CancellationTokenSource(const CancellationTokenSource &other) = delete;
+ CancellationTokenSource &operator=(const CancellationTokenSource &other) = delete;
+ ~CancellationTokenSource() {
+ cancel();
+ }
+
+ CancellationToken get_cancellation_token() {
+ if (!token_) {
+ token_ = std::make_shared<detail::RawCancellationToken>();
+ }
+ return CancellationToken(token_);
+ }
+ void cancel() {
+ if (!token_) {
+ return;
+ }
+ token_->is_canceled_.store(true, std::memory_order_release);
+ token_.reset();
+ }
+
+ private:
+ std::shared_ptr<detail::RawCancellationToken> token_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ChainScheduler.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ChainScheduler.h
new file mode 100644
index 0000000000..721a22e137
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ChainScheduler.h
@@ -0,0 +1,376 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/algorithm.h"
+#include "td/utils/common.h"
+#include "td/utils/Container.h"
+#include "td/utils/FlatHashSet.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/List.h"
+#include "td/utils/logging.h"
+#include "td/utils/optional.h"
+#include "td/utils/Span.h"
+#include "td/utils/StringBuilder.h"
+#include "td/utils/VectorQueue.h"
+
+#include <functional>
+#include <unordered_map>
+
+namespace td {
+
+struct ChainSchedulerBase {
+ struct TaskWithParents {
+ uint64 task_id{};
+ vector<uint64> parents;
+ };
+};
+
+template <class ExtraT = Unit>
+class ChainScheduler final : public ChainSchedulerBase {
+ public:
+ using TaskId = uint64;
+ using ChainId = uint64;
+
+ TaskId create_task(Span<ChainId> chains, ExtraT extra = {});
+
+ ExtraT *get_task_extra(TaskId task_id);
+
+ optional<TaskWithParents> start_next_task();
+
+ void pause_task(TaskId task_id);
+
+ void finish_task(TaskId task_id);
+
+ void reset_task(TaskId task_id);
+
+ template <class F>
+ void for_each(F &&f) {
+ tasks_.for_each([&f](auto, Task &task) { f(task.extra); });
+ }
+
+ template <class F>
+ void for_each_dependent(TaskId task_id, F &&f) {
+ auto *task = tasks_.get(task_id);
+ CHECK(task != nullptr);
+ FlatHashSet<TaskId> visited;
+ bool check_for_collisions = task->chains.size() > 1;
+ for (TaskChainInfo &task_chain_info : task->chains) {
+ ChainInfo &chain_info = *task_chain_info.chain_info;
+ chain_info.chain.foreach_child(&task_chain_info.chain_node, [&](TaskId task_id, uint64) {
+ if (check_for_collisions && !visited.insert(task_id).second) {
+ return;
+ }
+ f(task_id);
+ });
+ }
+ }
+
+ private:
+ struct ChainNode : ListNode {
+ TaskId task_id{};
+ uint64 generation{};
+ };
+
+ class Chain {
+ public:
+ void add_task(ChainNode *node) {
+ head_.put_back(node);
+ }
+
+ optional<TaskId> get_first() {
+ if (head_.empty()) {
+ return {};
+ }
+ return static_cast<ChainNode &>(*head_.get_next()).task_id;
+ }
+
+ optional<TaskId> get_child(ChainNode *chain_node) {
+ if (chain_node->get_next() == head_.end()) {
+ return {};
+ }
+ return static_cast<ChainNode &>(*chain_node->get_next()).task_id;
+ }
+ optional<ChainNode *> get_parent(ChainNode *chain_node) {
+ if (chain_node->get_prev() == head_.end()) {
+ return {};
+ }
+ return static_cast<ChainNode *>(chain_node->get_prev());
+ }
+
+ void finish_task(ChainNode *node) {
+ node->remove();
+ }
+
+ bool empty() const {
+ return head_.empty();
+ }
+
+ void foreach(std::function<void(TaskId, uint64)> f) const {
+ for (auto it = head_.begin(); it != head_.end(); it = it->get_next()) {
+ auto &node = static_cast<const ChainNode &>(*it);
+ f(node.task_id, node.generation);
+ }
+ }
+ void foreach_child(ListNode *start_node, std::function<void(TaskId, uint64)> f) const {
+ for (auto it = start_node; it != head_.end(); it = it->get_next()) {
+ auto &node = static_cast<const ChainNode &>(*it);
+ f(node.task_id, node.generation);
+ }
+ }
+
+ private:
+ ListNode head_;
+ };
+ struct ChainInfo {
+ Chain chain;
+ uint32 active_tasks{};
+ uint64 generation{1};
+ };
+ struct TaskChainInfo {
+ ChainNode chain_node;
+ ChainId chain_id{};
+ ChainInfo *chain_info{};
+ };
+ struct Task {
+ enum class State { Pending, Active, Paused } state{State::Pending};
+ vector<TaskChainInfo> chains;
+ ExtraT extra;
+ };
+ std::unordered_map<ChainId, ChainInfo, Hash<ChainId>> chains_;
+ std::unordered_map<ChainId, TaskId, Hash<ChainId>> limited_tasks_;
+ Container<Task> tasks_;
+ VectorQueue<TaskId> pending_tasks_;
+
+ void try_start_task(TaskId task_id) {
+ auto *task = tasks_.get(task_id);
+ CHECK(task != nullptr);
+ if (task->state != Task::State::Pending) {
+ return;
+ }
+ for (TaskChainInfo &task_chain_info : task->chains) {
+ auto o_parent = task_chain_info.chain_info->chain.get_parent(&task_chain_info.chain_node);
+
+ if (o_parent) {
+ if (o_parent.value()->generation != task_chain_info.chain_info->generation) {
+ return;
+ }
+ }
+
+ if (task_chain_info.chain_info->active_tasks >= 10) {
+ limited_tasks_[task_chain_info.chain_id] = task_id;
+ return;
+ }
+ }
+
+ do_start_task(task_id, task);
+ }
+
+ void do_start_task(TaskId task_id, Task *task) {
+ for (TaskChainInfo &task_chain_info : task->chains) {
+ ChainInfo &chain_info = chains_[task_chain_info.chain_id];
+ chain_info.active_tasks++;
+ task_chain_info.chain_node.generation = chain_info.generation;
+ }
+ task->state = Task::State::Active;
+
+ pending_tasks_.push(task_id);
+ for_each_child(task, [&](TaskId task_id) { try_start_task(task_id); });
+ }
+
+ template <class F>
+ void for_each_child(Task *task, F &&f) {
+ for (TaskChainInfo &task_chain_info : task->chains) {
+ ChainInfo &chain_info = *task_chain_info.chain_info;
+ auto o_child = chain_info.chain.get_child(&task_chain_info.chain_node);
+ if (o_child) {
+ f(o_child.value());
+ }
+ }
+ }
+
+ void inactivate_task(TaskId task_id, bool failed) {
+ LOG(DEBUG) << "Inactivate " << task_id << " " << (failed ? "failed" : "finished");
+ auto *task = tasks_.get(task_id);
+ CHECK(task != nullptr);
+ bool was_active = task->state == Task::State::Active;
+ task->state = Task::State::Pending;
+ for (TaskChainInfo &task_chain_info : task->chains) {
+ ChainInfo &chain_info = *task_chain_info.chain_info;
+ if (was_active) {
+ chain_info.active_tasks--;
+ }
+ if (was_active && failed) {
+ chain_info.generation = td::max(chain_info.generation, task_chain_info.chain_node.generation + 1);
+ }
+
+ auto it = limited_tasks_.find(task_chain_info.chain_id);
+ if (it != limited_tasks_.end()) {
+ auto limited_task_id = it->second;
+ limited_tasks_.erase(it);
+ if (limited_task_id != task_id) {
+ try_start_task_later(limited_task_id);
+ }
+ }
+
+ auto o_first = chain_info.chain.get_first();
+ if (o_first) {
+ auto first_task_id = o_first.unwrap();
+ if (first_task_id != task_id) {
+ try_start_task_later(first_task_id);
+ }
+ }
+ }
+ }
+
+ void finish_chain_task(TaskChainInfo &task_chain_info) {
+ auto &chain = task_chain_info.chain_info->chain;
+ chain.finish_task(&task_chain_info.chain_node);
+ if (chain.empty()) {
+ chains_.erase(task_chain_info.chain_id);
+ }
+ }
+
+ vector<TaskId> to_start_;
+
+ void try_start_task_later(TaskId task_id) {
+ LOG(DEBUG) << "Start later " << task_id;
+ to_start_.push_back(task_id);
+ }
+
+ void flush_try_start_task() {
+ auto moved_to_start = std::move(to_start_);
+ for (auto task_id : moved_to_start) {
+ try_start_task(task_id);
+ }
+ CHECK(to_start_.empty());
+ }
+
+ template <class ExtraTT>
+ friend StringBuilder &operator<<(StringBuilder &sb, ChainScheduler<ExtraTT> &scheduler);
+};
+
+template <class ExtraT>
+typename ChainScheduler<ExtraT>::TaskId ChainScheduler<ExtraT>::create_task(Span<ChainScheduler::ChainId> chains,
+ ExtraT extra) {
+ auto task_id = tasks_.create();
+ Task &task = *tasks_.get(task_id);
+ task.extra = std::move(extra);
+ task.chains = transform(chains, [&](auto chain_id) {
+ TaskChainInfo task_chain_info;
+ ChainInfo &chain_info = chains_[chain_id];
+ task_chain_info.chain_id = chain_id;
+ task_chain_info.chain_info = &chain_info;
+ task_chain_info.chain_node.task_id = task_id;
+ task_chain_info.chain_node.generation = 0;
+ return task_chain_info;
+ });
+
+ for (TaskChainInfo &task_chain_info : task.chains) {
+ ChainInfo &chain_info = *task_chain_info.chain_info;
+ chain_info.chain.add_task(&task_chain_info.chain_node);
+ }
+
+ try_start_task(task_id);
+ return task_id;
+}
+
+// TODO: return reference
+template <class ExtraT>
+ExtraT *ChainScheduler<ExtraT>::get_task_extra(ChainScheduler::TaskId task_id) { // may return nullptr
+ auto *task = tasks_.get(task_id);
+ if (task == nullptr) {
+ return nullptr;
+ }
+ return &task->extra;
+}
+
+template <class ExtraT>
+optional<ChainSchedulerBase::TaskWithParents> ChainScheduler<ExtraT>::start_next_task() {
+ if (pending_tasks_.empty()) {
+ return {};
+ }
+ auto task_id = pending_tasks_.pop();
+ TaskWithParents res;
+ res.task_id = task_id;
+ auto *task = tasks_.get(task_id);
+ CHECK(task != nullptr);
+ for (TaskChainInfo &task_chain_info : task->chains) {
+ Chain &chain = task_chain_info.chain_info->chain;
+ auto o_parent = chain.get_parent(&task_chain_info.chain_node);
+ if (o_parent) {
+ res.parents.push_back(o_parent.value()->task_id);
+ }
+ }
+ return res;
+}
+
+template <class ExtraT>
+void ChainScheduler<ExtraT>::finish_task(ChainScheduler::TaskId task_id) {
+ auto *task = tasks_.get(task_id);
+ CHECK(task != nullptr);
+ CHECK(to_start_.empty());
+
+ inactivate_task(task_id, false);
+
+ for_each_child(task, [&](TaskId task_id) { try_start_task_later(task_id); });
+
+ for (TaskChainInfo &task_chain_info : task->chains) {
+ finish_chain_task(task_chain_info);
+ }
+
+ tasks_.erase(task_id);
+ flush_try_start_task();
+}
+
+template <class ExtraT>
+void ChainScheduler<ExtraT>::reset_task(ChainScheduler::TaskId task_id) {
+ CHECK(to_start_.empty());
+ auto *task = tasks_.get(task_id);
+ CHECK(task != nullptr);
+ inactivate_task(task_id, true);
+ try_start_task_later(task_id);
+ flush_try_start_task();
+}
+
+template <class ExtraT>
+void ChainScheduler<ExtraT>::pause_task(TaskId task_id) {
+ auto *task = tasks_.get(task_id);
+ CHECK(task != nullptr);
+ inactivate_task(task_id, true);
+ task->state = Task::State::Paused;
+ flush_try_start_task();
+}
+
+template <class ExtraT>
+StringBuilder &operator<<(StringBuilder &sb, ChainScheduler<ExtraT> &scheduler) {
+ // 1 print chains
+ sb << '\n';
+ for (auto &it : scheduler.chains_) {
+ sb << "ChainId{" << it.first << "}";
+ sb << " active_cnt = " << it.second.active_tasks;
+ sb << " g = " << it.second.generation;
+ sb << ':';
+ it.second.chain.foreach(
+ [&](auto task_id, auto generation) { sb << ' ' << *scheduler.get_task_extra(task_id) << ':' << generation; });
+ sb << '\n';
+ }
+ scheduler.tasks_.for_each([&](auto id, auto &task) {
+ sb << "Task: " << task.extra;
+ sb << " state = " << static_cast<int>(task.state);
+ for (auto &task_chain_info : task.chains) {
+ sb << " g = " << task_chain_info.chain_node.generation;
+ if (task_chain_info.chain_info->generation != task_chain_info.chain_node.generation) {
+ sb << " chain_g = " << task_chain_info.chain_info->generation;
+ }
+ }
+ sb << '\n';
+ });
+ return sb;
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ChangesProcessor.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ChangesProcessor.h
index 9342f45a8b..669111a2ff 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/ChangesProcessor.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ChangesProcessor.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Closure.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Closure.h
index 718f930b8a..345f1b1f89 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Closure.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Closure.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,9 +8,7 @@
#include "td/utils/common.h"
#include "td/utils/invoke.h"
-#include "td/utils/logging.h"
-#include <cstdlib>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -54,8 +52,7 @@
// If delay is needed, just std::forward data to temporary storage, and std::move them when call is executed.
//
//
-// create_immediate_closure(&Actor::func, arg1, arg2, ..., argn).run(actor)
-// to_delayed_closure(std::move(immediate)).run(actor)
+// create_immediate_closure(&ActorT::func, arg1, arg2, ..., argn).run(actor)
namespace td {
template <class ActorT, class FunctionT, class... ArgsT>
@@ -68,22 +65,22 @@ class ImmediateClosure {
friend Delayed;
using ActorType = ActorT;
- void run(ActorT *actor) {
- mem_call_tuple(actor, func, std::move(args));
- }
-
// no &&. just save references as references.
- explicit ImmediateClosure(FunctionT func, ArgsT... args) : func(func), args(std::forward<ArgsT>(args)...) {
+ explicit ImmediateClosure(FunctionT func, ArgsT... args) : args(func, std::forward<ArgsT>(args)...) {
}
private:
- FunctionT func;
- std::tuple<ArgsT...> args;
+ std::tuple<FunctionT, ArgsT...> args;
+
+ public:
+ auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) {
+ return mem_call_tuple(actor, std::move(args));
+ }
};
template <class ActorT, class ResultT, class... DestArgsT, class... SrcArgsT>
ImmediateClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...> create_immediate_closure(
- ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&... args) {
+ ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&...args) {
return ImmediateClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...>(func,
std::forward<SrcArgsT>(args)...);
}
@@ -92,21 +89,11 @@ template <class ActorT, class FunctionT, class... ArgsT>
class DelayedClosure {
public:
using ActorType = ActorT;
- using Delayed = DelayedClosure<ActorT, FunctionT, ArgsT...>;
- void run(ActorT *actor) {
- mem_call_tuple(actor, func, std::move(args));
+ explicit DelayedClosure(ImmediateClosure<ActorT, FunctionT, ArgsT...> &&other) : args(std::move(other.args)) {
}
- DelayedClosure clone() const {
- return do_clone(*this);
- }
-
- explicit DelayedClosure(ImmediateClosure<ActorT, FunctionT, ArgsT...> &&other)
- : func(std::move(other.func)), args(std::move(other.args)) {
- }
-
- explicit DelayedClosure(FunctionT func, ArgsT... args) : func(func), args(std::forward<ArgsT>(args)...) {
+ explicit DelayedClosure(FunctionT func, ArgsT... args) : args(func, std::forward<ArgsT>(args)...) {
}
template <class F>
@@ -115,53 +102,16 @@ class DelayedClosure {
}
private:
- using ArgsStorageT = std::tuple<typename std::decay<ArgsT>::type...>;
-
- FunctionT func;
- ArgsStorageT args;
+ std::tuple<FunctionT, typename std::decay<ArgsT>::type...> args;
- template <class FromActorT, class FromFunctionT, class... FromArgsT>
- explicit DelayedClosure(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
- std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0)
- : func(other.func), args(other.args) {
- }
-
- template <class FromActorT, class FromFunctionT, class... FromArgsT>
- explicit DelayedClosure(
- const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
- std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0) {
- LOG(FATAL) << "Deleted constructor";
- std::abort();
- }
-
- template <class FromActorT, class FromFunctionT, class... FromArgsT>
- std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
- DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
- do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
- LOG(FATAL) << "Trying to clone DelayedClosure that contains noncopyable elements";
- std::abort();
- }
-
- template <class FromActorT, class FromFunctionT, class... FromArgsT>
- std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
- DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
- do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
- return DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>(value);
+ public:
+ auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) {
+ return mem_call_tuple(actor, std::move(args));
}
};
-template <class... ArgsT>
-typename ImmediateClosure<ArgsT...>::Delayed to_delayed_closure(ImmediateClosure<ArgsT...> &&other) {
- return typename ImmediateClosure<ArgsT...>::Delayed(std::move(other));
-}
-
-template <class... ArgsT>
-DelayedClosure<ArgsT...> to_delayed_closure(DelayedClosure<ArgsT...> &&other) {
- return std::move(other);
-}
-
template <class ActorT, class ResultT, class... DestArgsT, class... SrcArgsT>
-auto create_delayed_closure(ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&... args) {
+auto create_delayed_closure(ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&...args) {
return DelayedClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...>(func,
std::forward<SrcArgsT>(args)...);
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/CombinedLog.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/CombinedLog.h
new file mode 100644
index 0000000000..f2fe36069e
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/CombinedLog.h
@@ -0,0 +1,86 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/algorithm.h"
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/Slice.h"
+
+namespace td {
+
+class CombinedLog final : public LogInterface {
+ public:
+ void set_first(LogInterface *first) {
+ first_ = first;
+ }
+
+ void set_second(LogInterface *second) {
+ second_ = second;
+ }
+
+ void set_first_verbosity_level(int new_verbosity_level) {
+ first_verbosity_level_ = new_verbosity_level;
+ }
+
+ void set_second_verbosity_level(int new_verbosity_level) {
+ second_verbosity_level_ = new_verbosity_level;
+ }
+
+ const LogInterface *get_first() const {
+ return first_;
+ }
+
+ const LogInterface *get_second() const {
+ return second_;
+ }
+
+ int get_first_verbosity_level() const {
+ return first_verbosity_level_;
+ }
+
+ int get_second_verbosity_level() const {
+ return second_verbosity_level_;
+ }
+
+ private:
+ LogInterface *first_ = nullptr;
+ int first_verbosity_level_ = VERBOSITY_NAME(FATAL);
+ LogInterface *second_ = nullptr;
+ int second_verbosity_level_ = VERBOSITY_NAME(FATAL);
+
+ void do_append(int log_level, CSlice slice) final {
+ if (first_ && log_level <= first_verbosity_level_) {
+ first_->do_append(log_level, slice);
+ }
+ if (second_ && log_level <= second_verbosity_level_) {
+ second_->do_append(log_level, slice);
+ }
+ }
+
+ void after_rotation() final {
+ if (first_) {
+ first_->after_rotation();
+ }
+ if (second_) {
+ second_->after_rotation();
+ }
+ }
+
+ vector<string> get_file_paths() final {
+ vector<string> result;
+ if (first_) {
+ ::td::append(result, first_->get_file_paths());
+ }
+ if (second_) {
+ ::td::append(result, second_->get_file_paths());
+ }
+ return result;
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ConcurrentHashTable.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ConcurrentHashTable.h
new file mode 100644
index 0000000000..f3f0bcfa92
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ConcurrentHashTable.h
@@ -0,0 +1,322 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/HazardPointers.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/thread_local.h"
+
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
+namespace td {
+
+// AtomicHashArray<KeyT, ValueT>
+// Building block for other concurrent hash maps
+//
+// Support one operation:
+// template <class F>
+// bool with_value(KeyT key, bool should_create, F &&func);
+//
+// Finds slot for key, and call func(value)
+// Creates slot if should_create is true.
+// Returns true if func was called.
+//
+// Concurrent calls with the same key may result in concurrent calls to func(value)
+// It is responsibility of the caller to handle such races.
+//
+// Key should already be random
+// It is responsibility of the caller to provide unique random key.
+// One may use injective hash function, or handle collisions in some other way.
+
+template <class KeyT, class ValueT>
+class AtomicHashArray {
+ public:
+ explicit AtomicHashArray(size_t n) : nodes_(n) {
+ }
+ struct Node {
+ std::atomic<KeyT> key{KeyT{}};
+ ValueT value{};
+ };
+ size_t size() const {
+ return nodes_.size();
+ }
+ Node &node_at(size_t i) {
+ return nodes_[i];
+ }
+ static KeyT empty_key() {
+ return KeyT{};
+ }
+
+ template <class F>
+ bool with_value(KeyT key, bool should_create, F &&f) {
+ DCHECK(key != empty_key());
+ auto pos = static_cast<size_t>(key) % nodes_.size();
+ auto n = td::min(td::max(static_cast<size_t>(300), nodes_.size() / 16 + 2), nodes_.size());
+
+ for (size_t i = 0; i < n; i++) {
+ pos++;
+ if (pos >= nodes_.size()) {
+ pos = 0;
+ }
+ auto &node = nodes_[pos];
+ while (true) {
+ auto node_key = node.key.load(std::memory_order_acquire);
+ if (node_key == empty_key()) {
+ if (!should_create) {
+ return false;
+ }
+ KeyT expected_key = empty_key();
+ if (node.key.compare_exchange_strong(expected_key, key, std::memory_order_relaxed,
+ std::memory_order_relaxed)) {
+ f(node.value);
+ return true;
+ }
+ } else if (node_key == key) {
+ f(node.value);
+ return true;
+ } else {
+ break;
+ }
+ }
+ }
+ return false;
+ }
+
+ private:
+ std::vector<Node> nodes_;
+};
+
+// Simple concurrent hash map with multiple limitations
+template <class KeyT, class ValueT>
+class ConcurrentHashMap {
+ using HashMap = AtomicHashArray<KeyT, std::atomic<ValueT>>;
+ static HazardPointers<HashMap> hp_;
+
+ public:
+ explicit ConcurrentHashMap(size_t n = 32) {
+ n = 1;
+ hash_map_.store(make_unique<HashMap>(n).release());
+ }
+ ConcurrentHashMap(const ConcurrentHashMap &) = delete;
+ ConcurrentHashMap &operator=(const ConcurrentHashMap &) = delete;
+ ConcurrentHashMap(ConcurrentHashMap &&) = delete;
+ ConcurrentHashMap &operator=(ConcurrentHashMap &&) = delete;
+ ~ConcurrentHashMap() {
+ unique_ptr<HashMap>(hash_map_.load()).reset();
+ }
+
+ static std::string get_name() {
+ return "ConcurrrentHashMap";
+ }
+
+ static KeyT empty_key() {
+ return KeyT{};
+ }
+ static ValueT empty_value() {
+ return ValueT{};
+ }
+ static ValueT migrate_value() {
+ return (ValueT)(1); // c-style conversion because reinterpret_cast<int>(1) is CE in MSVC
+ }
+
+ ValueT insert(KeyT key, ValueT value) {
+ CHECK(key != empty_key());
+ CHECK(value != migrate_value());
+ typename HazardPointers<HashMap>::Holder holder(hp_, get_thread_id(), 0);
+ while (true) {
+ auto hash_map = holder.protect(hash_map_);
+ if (!hash_map) {
+ do_migrate(nullptr);
+ continue;
+ }
+
+ bool ok = false;
+ ValueT inserted_value;
+ hash_map->with_value(key, true, [&](auto &node_value) {
+ ValueT expected_value = this->empty_value();
+ if (node_value.compare_exchange_strong(expected_value, value, std::memory_order_release,
+ std::memory_order_acquire)) {
+ ok = true;
+ inserted_value = value;
+ } else {
+ if (expected_value == this->migrate_value()) {
+ ok = false;
+ } else {
+ ok = true;
+ inserted_value = expected_value;
+ }
+ }
+ });
+ if (ok) {
+ return inserted_value;
+ }
+ do_migrate(hash_map);
+ }
+ }
+
+ ValueT find(KeyT key, ValueT value) {
+ typename HazardPointers<HashMap>::Holder holder(hp_, get_thread_id(), 0);
+ while (true) {
+ auto hash_map = holder.protect(hash_map_);
+ if (!hash_map) {
+ do_migrate(nullptr);
+ continue;
+ }
+
+ bool has_value = hash_map->with_value(
+ key, false, [&](auto &node_value) { value = node_value.load(std::memory_order_acquire); });
+ if (!has_value || value != migrate_value()) {
+ return value;
+ }
+ do_migrate(hash_map);
+ }
+ }
+
+ template <class F>
+ void for_each(F &&f) {
+ auto hash_map = hash_map_.load();
+ CHECK(hash_map);
+ auto size = hash_map->size();
+ for (size_t i = 0; i < size; i++) {
+ auto &node = hash_map->node_at(i);
+ auto key = node.key.load(std::memory_order_relaxed);
+ auto value = node.value.load(std::memory_order_relaxed);
+
+ if (key != empty_key()) {
+ CHECK(value != migrate_value());
+ if (value != empty_value()) {
+ f(key, value);
+ }
+ }
+ }
+ }
+
+ private:
+ // use no padding intentionally
+ std::atomic<HashMap *> hash_map_{nullptr};
+
+ std::mutex migrate_mutex_;
+ std::condition_variable migrate_cv_;
+
+ int migrate_cnt_{0};
+ int migrate_generation_{0};
+ HashMap *migrate_from_hash_map_{nullptr};
+ HashMap *migrate_to_hash_map_{nullptr};
+ struct Task {
+ size_t begin;
+ size_t end;
+ bool empty() const {
+ return begin >= end;
+ }
+ size_t size() const {
+ if (empty()) {
+ return 0;
+ }
+ return end - begin;
+ }
+ };
+
+ struct TaskCreator {
+ size_t chunk_size;
+ size_t size;
+ std::atomic<size_t> pos{0};
+ Task create() {
+ auto i = pos++;
+ auto begin = i * chunk_size;
+ auto end = begin + chunk_size;
+ if (end > size) {
+ end = size;
+ }
+ return {begin, end};
+ }
+ };
+ TaskCreator task_creator;
+
+ void do_migrate(HashMap *ptr) {
+ //LOG(ERROR) << "In do_migrate: " << ptr;
+ std::unique_lock<std::mutex> lock(migrate_mutex_);
+ if (hash_map_.load() != ptr) {
+ return;
+ }
+ init_migrate();
+ CHECK(!ptr || migrate_from_hash_map_ == ptr);
+ migrate_cnt_++;
+ auto migrate_generation = migrate_generation_;
+ lock.unlock();
+
+ run_migrate();
+
+ lock.lock();
+ migrate_cnt_--;
+ if (migrate_cnt_ == 0) {
+ finish_migrate();
+ }
+ migrate_cv_.wait(lock, [&] { return migrate_generation_ != migrate_generation; });
+ }
+
+ void finish_migrate() {
+ //LOG(ERROR) << "In finish_migrate";
+ hash_map_.store(migrate_to_hash_map_);
+ hp_.retire(get_thread_id(), migrate_from_hash_map_);
+ migrate_from_hash_map_ = nullptr;
+ migrate_to_hash_map_ = nullptr;
+ migrate_generation_++;
+ migrate_cv_.notify_all();
+ }
+
+ void init_migrate() {
+ if (migrate_from_hash_map_ != nullptr) {
+ return;
+ }
+ //LOG(ERROR) << "In init_migrate";
+ CHECK(migrate_cnt_ == 0);
+ migrate_generation_++;
+ migrate_from_hash_map_ = hash_map_.exchange(nullptr);
+ auto new_size = migrate_from_hash_map_->size() * 2;
+ migrate_to_hash_map_ = make_unique<HashMap>(new_size).release();
+ task_creator.chunk_size = 100;
+ task_creator.size = migrate_from_hash_map_->size();
+ task_creator.pos = 0;
+ }
+
+ void run_migrate() {
+ //LOG(ERROR) << "In run_migrate";
+ size_t cnt = 0;
+ while (true) {
+ auto task = task_creator.create();
+ cnt += task.size();
+ if (task.empty()) {
+ break;
+ }
+ run_task(task);
+ }
+ //LOG(ERROR) << "In run_migrate " << cnt;
+ }
+
+ void run_task(Task task) {
+ for (auto i = task.begin; i < task.end; i++) {
+ auto &node = migrate_from_hash_map_->node_at(i);
+ auto old_value = node.value.exchange(migrate_value(), std::memory_order_acq_rel);
+ if (old_value == 0) {
+ continue;
+ }
+ auto node_key = node.key.load(std::memory_order_relaxed);
+ //LOG(ERROR) << node_key << " " << node_key;
+ auto ok = migrate_to_hash_map_->with_value(
+ node_key, true, [&](auto &node_value) { node_value.store(old_value, std::memory_order_relaxed); });
+ LOG_CHECK(ok) << "Migration overflow";
+ }
+ }
+};
+
+template <class KeyT, class ValueT>
+HazardPointers<typename ConcurrentHashMap<KeyT, ValueT>::HashMap> ConcurrentHashMap<KeyT, ValueT>::hp_(64);
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Container.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Container.h
index 57b4bb4d16..418edfd56a 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Container.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Container.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,6 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include <limits>
@@ -107,8 +106,8 @@ class Container {
}
int32 decode_id(Id id) const {
- int32 slot_id = static_cast<int32>(id >> 32);
- uint32 generation = static_cast<uint32>(id);
+ auto slot_id = static_cast<int32>(id >> 32);
+ auto generation = static_cast<uint32>(id);
if (slot_id < 0 || slot_id >= static_cast<int32>(slots_.size())) {
return -1;
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Context.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Context.h
new file mode 100644
index 0000000000..e9cac2ed09
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Context.h
@@ -0,0 +1,44 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/port/thread_local.h"
+
+namespace td {
+
+template <class Impl>
+class Context {
+ public:
+ static Impl *get() {
+ return context_;
+ }
+ class Guard {
+ public:
+ explicit Guard(Impl *new_context) {
+ old_context_ = context_;
+ context_ = new_context;
+ }
+ ~Guard() {
+ context_ = old_context_;
+ }
+ Guard(const Guard &) = delete;
+ Guard &operator=(const Guard &) = delete;
+ Guard(Guard &&) = delete;
+ Guard &operator=(Guard &&) = delete;
+
+ private:
+ Impl *old_context_;
+ };
+
+ private:
+ static TD_THREAD_LOCAL Impl *context_;
+};
+
+template <class Impl>
+TD_THREAD_LOCAL Impl *Context<Impl>::context_;
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/DecTree.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/DecTree.h
new file mode 100644
index 0000000000..6044842f69
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/DecTree.h
@@ -0,0 +1,216 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Random.h"
+
+#include <functional>
+#include <utility>
+
+namespace td {
+
+template <typename KeyType, typename ValueType, typename Compare = std::less<KeyType>>
+class DecTree {
+ struct Node {
+ unique_ptr<Node> left_;
+ unique_ptr<Node> right_;
+ size_t size_;
+ KeyType key_;
+ ValueType value_;
+ uint32 y_;
+
+ void relax() {
+ size_ = 1;
+ if (left_ != nullptr) {
+ size_ += left_->size_;
+ }
+ if (right_ != nullptr) {
+ size_ += right_->size_;
+ }
+ }
+
+ Node(KeyType key, ValueType value, uint32 y) : size_(1), key_(std::move(key)), value_(std::move(value)), y_(y) {
+ }
+ };
+ unique_ptr<Node> root_;
+
+ static unique_ptr<Node> create_node(KeyType key, ValueType value, uint32 y) {
+ return make_unique<Node>(std::move(key), std::move(value), y);
+ }
+
+ static unique_ptr<Node> insert_node(unique_ptr<Node> Tree, KeyType key, ValueType value, uint32 y) {
+ if (Tree == nullptr) {
+ return create_node(std::move(key), std::move(value), y);
+ }
+ if (Tree->y_ < y) {
+ auto P = split_node(std::move(Tree), key);
+ auto T = create_node(std::move(key), std::move(value), y);
+ T->left_ = std::move(P.first);
+ T->right_ = std::move(P.second);
+ T->relax();
+ return T;
+ }
+ if (Compare()(key, Tree->key_)) {
+ Tree->left_ = insert_node(std::move(Tree->left_), std::move(key), std::move(value), y);
+ } else if (Compare()(Tree->key_, key)) {
+ Tree->right_ = insert_node(std::move(Tree->right_), std::move(key), std::move(value), y);
+ } else {
+ // ?? assert
+ }
+ Tree->relax();
+ return Tree;
+ }
+
+ static unique_ptr<Node> remove_node(unique_ptr<Node> Tree, const KeyType &key) {
+ if (Tree == nullptr) {
+ // ?? assert
+ return nullptr;
+ }
+ if (Compare()(key, Tree->key_)) {
+ Tree->left_ = remove_node(std::move(Tree->left_), key);
+ } else if (Compare()(Tree->key_, key)) {
+ Tree->right_ = remove_node(std::move(Tree->right_), key);
+ } else {
+ Tree = merge_node(std::move(Tree->left_), std::move(Tree->right_));
+ }
+ if (Tree != nullptr) {
+ Tree->relax();
+ }
+ return Tree;
+ }
+
+ static ValueType *get_node(unique_ptr<Node> &Tree, const KeyType &key) {
+ if (Tree == nullptr) {
+ return nullptr;
+ }
+ if (Compare()(key, Tree->key_)) {
+ return get_node(Tree->left_, key);
+ } else if (Compare()(Tree->key_, key)) {
+ return get_node(Tree->right_, key);
+ } else {
+ return &Tree->value_;
+ }
+ }
+
+ static ValueType *get_node_by_idx(unique_ptr<Node> &Tree, size_t idx) {
+ CHECK(Tree != nullptr);
+ auto s = (Tree->left_ != nullptr) ? Tree->left_->size_ : 0;
+ if (idx < s) {
+ return get_node_by_idx(Tree->left_, idx);
+ } else if (idx == s) {
+ return &Tree->value_;
+ } else {
+ return get_node_by_idx(Tree->right_, idx - s - 1);
+ }
+ }
+
+ static const ValueType *get_node(const unique_ptr<Node> &Tree, const KeyType &key) {
+ if (Tree == nullptr) {
+ return nullptr;
+ }
+ if (Compare()(key, Tree->key_)) {
+ return get_node(Tree->left_, key);
+ } else if (Compare()(Tree->key_, key)) {
+ return get_node(Tree->right_, key);
+ } else {
+ return &Tree->value_;
+ }
+ }
+
+ static const ValueType *get_node_by_idx(const unique_ptr<Node> &Tree, size_t idx) {
+ CHECK(Tree != nullptr);
+ auto s = (Tree->left_ != nullptr) ? Tree->left_->size_ : 0;
+ if (idx < s) {
+ return get_node_by_idx(Tree->left_, idx);
+ } else if (idx == s) {
+ return &Tree->value_;
+ } else {
+ return get_node_by_idx(Tree->right_, idx - s - 1);
+ }
+ }
+
+ static std::pair<unique_ptr<Node>, unique_ptr<Node>> split_node(unique_ptr<Node> Tree, const KeyType &key) {
+ if (Tree == nullptr) {
+ return {nullptr, nullptr};
+ }
+ if (Compare()(key, Tree->key_)) {
+ auto P = split_node(std::move(Tree->left_), key);
+ Tree->left_ = std::move(P.second);
+ Tree->relax();
+ P.second = std::move(Tree);
+ return P;
+ } else {
+ auto P = split_node(std::move(Tree->right_), key);
+ Tree->right_ = std::move(P.first);
+ Tree->relax();
+ P.first = std::move(Tree);
+ return P;
+ }
+ }
+
+ static unique_ptr<Node> merge_node(unique_ptr<Node> left, unique_ptr<Node> right) {
+ if (left == nullptr) {
+ return right;
+ }
+ if (right == nullptr) {
+ return left;
+ }
+ if (left->y_ < right->y_) {
+ right->left_ = merge_node(std::move(left), std::move(right->left_));
+ right->relax();
+ return right;
+ } else {
+ left->right_ = merge_node(std::move(left->right_), std::move(right));
+ left->relax();
+ return left;
+ }
+ }
+
+ public:
+ size_t size() const {
+ if (root_ == nullptr) {
+ return 0;
+ } else {
+ return root_->size_;
+ }
+ }
+ void insert(KeyType key, ValueType value) {
+ root_ = insert_node(std::move(root_), std::move(key), std::move(value), Random::fast_uint32());
+ }
+ void remove(const KeyType &key) {
+ root_ = remove_node(std::move(root_), key);
+ }
+ void reset() {
+ root_ = nullptr;
+ }
+ ValueType *get(const KeyType &key) {
+ return get_node(root_, key);
+ }
+ ValueType *get_random() {
+ if (size() == 0) {
+ return nullptr;
+ } else {
+ return get_node_by_idx(root_, Random::fast_uint32() % size());
+ }
+ }
+ const ValueType *get(const KeyType &key) const {
+ return get_node(root_, key);
+ }
+ const ValueType *get_random() const {
+ if (size() == 0) {
+ return nullptr;
+ } else {
+ return get_node_by_idx(root_, Random::fast_uint32() % size());
+ }
+ }
+ bool exists(const KeyType &key) const {
+ return get_node(root_, key) != nullptr;
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Destructor.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Destructor.h
new file mode 100644
index 0000000000..ced4a8eca8
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Destructor.h
@@ -0,0 +1,52 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#include <memory>
+#include <utility>
+
+namespace td {
+
+class Destructor {
+ public:
+ Destructor() = default;
+ Destructor(const Destructor &other) = delete;
+ Destructor &operator=(const Destructor &other) = delete;
+ Destructor(Destructor &&other) = default;
+ Destructor &operator=(Destructor &&other) = default;
+ virtual ~Destructor() = default;
+};
+
+template <class F>
+class LambdaDestructor final : public Destructor {
+ public:
+ explicit LambdaDestructor(F &&f) : f_(std::move(f)) {
+ }
+ LambdaDestructor(const LambdaDestructor &other) = delete;
+ LambdaDestructor &operator=(const LambdaDestructor &other) = delete;
+ LambdaDestructor(LambdaDestructor &&other) = default;
+ LambdaDestructor &operator=(LambdaDestructor &&other) = default;
+ ~LambdaDestructor() final {
+ f_();
+ }
+
+ private:
+ F f_;
+};
+
+template <class F>
+auto create_destructor(F &&f) {
+ return make_unique<LambdaDestructor<F>>(std::forward<F>(f));
+}
+template <class F>
+auto create_shared_destructor(F &&f) {
+ return std::make_shared<LambdaDestructor<F>>(std::forward<F>(f));
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Enumerator.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Enumerator.h
index ca7c0493ff..367bc9fa8c 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Enumerator.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Enumerator.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,9 +7,9 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
-#include "td/utils/misc.h"
+#include "td/utils/WaitFreeVector.h"
+#include <limits>
#include <map>
#include <tuple>
@@ -21,7 +21,8 @@ class Enumerator {
using Key = int32;
Key add(ValueT v) {
- int32 next_id = narrow_cast<int32>(arr_.size() + 1);
+ CHECK(arr_.size() < static_cast<size_t>(std::numeric_limits<int32>::max() - 1));
+ auto next_id = static_cast<int32>(arr_.size() + 1);
bool was_inserted;
decltype(map_.begin()) it;
std::tie(it, was_inserted) = map_.emplace(std::move(v), next_id);
@@ -37,9 +38,18 @@ class Enumerator {
return *arr_[pos];
}
+ size_t size() const {
+ CHECK(map_.size() == arr_.size());
+ return arr_.size();
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+
private:
std::map<ValueT, int32> map_;
- std::vector<const ValueT *> arr_;
+ WaitFreeVector<const ValueT *> arr_;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/EpochBasedMemoryReclamation.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/EpochBasedMemoryReclamation.h
new file mode 100644
index 0000000000..a11d307672
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/EpochBasedMemoryReclamation.h
@@ -0,0 +1,201 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/sleep.h"
+
+#include <atomic>
+#include <memory>
+
+namespace td {
+
+template <class T>
+class EpochBasedMemoryReclamation {
+ public:
+ EpochBasedMemoryReclamation(const EpochBasedMemoryReclamation &other) = delete;
+ EpochBasedMemoryReclamation &operator=(const EpochBasedMemoryReclamation &other) = delete;
+ EpochBasedMemoryReclamation(EpochBasedMemoryReclamation &&other) = delete;
+ EpochBasedMemoryReclamation &operator=(EpochBasedMemoryReclamation &&other) = delete;
+ ~EpochBasedMemoryReclamation() = default;
+
+ class Locker {
+ public:
+ Locker(size_t thread_id, EpochBasedMemoryReclamation *ebmr) : thread_id_(thread_id), ebmr_(ebmr) {
+ }
+ Locker(const Locker &other) = delete;
+ Locker &operator=(const Locker &other) = delete;
+ Locker(Locker &&other) = default;
+ Locker &operator=(Locker &&other) = delete;
+
+ ~Locker() {
+ if (ebmr_) {
+ retire_sync();
+ unlock();
+ (void)ebmr_.release();
+ }
+ }
+ void lock() {
+ DCHECK(ebmr_);
+ ebmr_->lock(thread_id_);
+ }
+ void unlock() {
+ DCHECK(ebmr_);
+ ebmr_->unlock(thread_id_);
+ }
+
+ void retire_sync() {
+ ebmr_->retire_sync(thread_id_);
+ }
+
+ void retire() {
+ ebmr_->retire(thread_id_);
+ }
+
+ void retire(T *ptr) {
+ ebmr_->retire(thread_id_, ptr);
+ }
+
+ private:
+ size_t thread_id_;
+ struct Never {
+ template <class S>
+ void operator()(S *) const {
+ UNREACHABLE();
+ }
+ };
+ std::unique_ptr<EpochBasedMemoryReclamation, Never> ebmr_;
+ };
+
+ explicit EpochBasedMemoryReclamation(size_t threads_n) : threads_(threads_n) {
+ }
+
+ Locker get_locker(size_t thread_id) {
+ return Locker{thread_id, this};
+ }
+
+ size_t to_delete_size_unsafe() const {
+ size_t res = 0;
+ for (auto &thread_data : threads_) {
+ // LOG(ERROR) << "---" << thread_data.epoch.load() / 2;
+ for (size_t i = 0; i < MAX_BAGS; i++) {
+ res += thread_data.to_delete[i].size();
+ // LOG(ERROR) << thread_data.to_delete[i].size();
+ }
+ }
+ return res;
+ }
+
+ private:
+ static constexpr size_t MAX_BAGS = 3;
+ struct ThreadData {
+ std::atomic<int64> epoch{1};
+ char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<int64>)];
+
+ size_t to_skip{0};
+ size_t checked_thread_i{0};
+ size_t bag_i{0};
+ std::vector<unique_ptr<T>> to_delete[MAX_BAGS];
+ char pad2[TD_CONCURRENCY_PAD - sizeof(std::vector<unique_ptr<T>>) * MAX_BAGS];
+
+ void rotate_bags() {
+ bag_i = (bag_i + 1) % MAX_BAGS;
+ to_delete[bag_i].clear();
+ }
+
+ void set_epoch(int64 new_epoch) {
+ //LOG(ERROR) << new_epoch;
+ if (epoch.load(std::memory_order_relaxed) / 2 != new_epoch) {
+ checked_thread_i = 0;
+ to_skip = 0;
+ rotate_bags();
+ }
+ epoch = new_epoch * 2;
+ }
+
+ void idle() {
+ epoch.store(epoch.load(std::memory_order_relaxed) | 1);
+ }
+
+ size_t undeleted() const {
+ size_t res = 0;
+ for (size_t i = 0; i < MAX_BAGS; i++) {
+ res += to_delete[i].size();
+ }
+ return res;
+ }
+ };
+ std::vector<ThreadData> threads_;
+ char pad[TD_CONCURRENCY_PAD - sizeof(std::vector<ThreadData>)];
+
+ std::atomic<int64> epoch_{1};
+ char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<int64>)];
+
+ void lock(size_t thread_id) {
+ auto &data = threads_[thread_id];
+ auto epoch = epoch_.load();
+ data.set_epoch(epoch);
+
+ if (data.to_skip == 0) {
+ data.to_skip = 30;
+ step_check(data);
+ } else {
+ data.to_skip--;
+ }
+ }
+
+ void unlock(size_t thread_id) {
+ //LOG(ERROR) << "UNLOCK";
+ auto &data = threads_[thread_id];
+ data.idle();
+ }
+
+ bool step_check(ThreadData &data) {
+ auto epoch = data.epoch.load(std::memory_order_relaxed) / 2;
+ auto checked_thread_epoch = threads_[data.checked_thread_i].epoch.load();
+ if (checked_thread_epoch % 2 == 1 || checked_thread_epoch / 2 == epoch) {
+ data.checked_thread_i++;
+ if (data.checked_thread_i == threads_.size()) {
+ if (epoch_.compare_exchange_strong(epoch, epoch + 1)) {
+ data.set_epoch(epoch + 1);
+ } else {
+ data.set_epoch(epoch);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ void retire_sync(size_t thread_id) {
+ auto &data = threads_[thread_id];
+
+ while (true) {
+ retire(thread_id);
+ data.idle();
+ if (data.undeleted() == 0) {
+ break;
+ }
+ usleep_for(1000);
+ }
+ }
+
+ void retire(size_t thread_id) {
+ auto &data = threads_[thread_id];
+ data.set_epoch(epoch_.load());
+ while (step_check(data) && data.undeleted() != 0) {
+ }
+ }
+
+ void retire(size_t thread_id, T *ptr) {
+ auto &data = threads_[thread_id];
+ data.to_delete[data.bag_i].push_back(unique_ptr<T>{ptr});
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.cpp
new file mode 100644
index 0000000000..3a410bd0d5
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.cpp
@@ -0,0 +1,20 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/ExitGuard.h"
+
+#include "td/utils/logging.h"
+
+namespace td {
+
+std::atomic<bool> ExitGuard::is_exited_{false};
+
+ExitGuard::~ExitGuard() {
+ is_exited_.store(true, std::memory_order_relaxed);
+ set_verbosity_level(VERBOSITY_NAME(FATAL));
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.h
new file mode 100644
index 0000000000..dd721fb5b9
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ExitGuard.h
@@ -0,0 +1,30 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include <atomic>
+
+namespace td {
+
+class ExitGuard {
+ public:
+ ExitGuard() = default;
+ ExitGuard(ExitGuard &&) = delete;
+ ExitGuard &operator=(ExitGuard &&) = delete;
+ ExitGuard(const ExitGuard &) = delete;
+ ExitGuard &operator=(const ExitGuard &) = delete;
+ ~ExitGuard();
+
+ static bool is_exited() {
+ return is_exited_.load(std::memory_order_relaxed);
+ }
+
+ private:
+ static std::atomic<bool> is_exited_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.cpp
index e3c84f1713..a16731442a 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,85 +8,133 @@
#include "td/utils/common.h"
#include "td/utils/logging.h"
-#include "td/utils/port/Fd.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/port/path.h"
+#include "td/utils/port/StdStreams.h"
+#include "td/utils/port/thread_local.h"
#include "td/utils/Slice.h"
-
-#include <limits>
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/Time.h"
namespace td {
-bool FileLog::init(string path, int64 rotate_threshold) {
+Status FileLog::init(string path, int64 rotate_threshold, bool redirect_stderr) {
+ if (path.empty()) {
+ return Status::Error("Log file path must be non-empty");
+ }
if (path == path_) {
set_rotate_threshold(rotate_threshold);
- return true;
+ return Status::OK();
}
- auto r_fd = FileFd::open(path, FileFd::Create | FileFd::Write | FileFd::Append);
- if (r_fd.is_error()) {
- LOG(ERROR) << "Can't open log: " << r_fd.error();
- return false;
- }
+ TRY_RESULT(fd, FileFd::open(path, FileFd::Create | FileFd::Write | FileFd::Append));
fd_.close();
- fd_ = r_fd.move_as_ok();
- Fd::duplicate(fd_.get_fd(), Fd::Stderr()).ignore();
+ fd_ = std::move(fd);
+ if (!Stderr().empty() && redirect_stderr) {
+ fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
+ }
- path_ = std::move(path);
- size_ = fd_.get_size();
+ auto r_path = realpath(path, true);
+ if (r_path.is_error()) {
+ path_ = std::move(path);
+ } else {
+ path_ = r_path.move_as_ok();
+ }
+ TRY_RESULT_ASSIGN(size_, fd_.get_size());
rotate_threshold_ = rotate_threshold;
- return true;
+ redirect_stderr_ = redirect_stderr;
+ return Status::OK();
+}
+
+Slice FileLog::get_path() const {
+ return path_;
+}
+
+vector<string> FileLog::get_file_paths() {
+ vector<string> result;
+ if (!path_.empty()) {
+ result.push_back(path_);
+ result.push_back(PSTRING() << path_ << ".old");
+ }
+ return result;
}
void FileLog::set_rotate_threshold(int64 rotate_threshold) {
rotate_threshold_ = rotate_threshold;
}
-void FileLog::append(CSlice cslice, int log_level) {
- Slice slice = cslice;
+int64 FileLog::get_rotate_threshold() const {
+ return rotate_threshold_;
+}
+
+bool FileLog::get_redirect_stderr() const {
+ return redirect_stderr_;
+}
+
+void FileLog::do_append(int log_level, CSlice slice) {
+ auto start_time = Time::now();
+ if (size_ > rotate_threshold_ || want_rotate_.load(std::memory_order_relaxed)) {
+ auto status = rename(path_, PSLICE() << path_ << ".old");
+ if (status.is_error()) {
+ process_fatal_error(PSLICE() << status << " in " << __FILE__ << " at " << __LINE__ << '\n');
+ }
+ do_after_rotation();
+ }
while (!slice.empty()) {
+ if (redirect_stderr_) {
+ while (has_log_guard()) {
+ // spin
+ }
+ }
auto r_size = fd_.write(slice);
if (r_size.is_error()) {
- process_fatal_error(r_size.error().message());
+ process_fatal_error(PSLICE() << r_size.error() << " in " << __FILE__ << " at " << __LINE__ << '\n');
}
auto written = r_size.ok();
size_ += static_cast<int64>(written);
slice.remove_prefix(written);
}
- if (log_level == VERBOSITY_NAME(FATAL)) {
- process_fatal_error(cslice);
- }
-
- if (size_ > rotate_threshold_) {
- auto status = rename(path_, path_ + ".old");
- if (status.is_error()) {
- process_fatal_error(status.message());
- }
- do_rotate();
+ auto total_time = Time::now() - start_time;
+ if (total_time >= 0.1 && log_level >= 1) {
+ auto thread_id = get_thread_id();
+ auto r_size = fd_.write(PSLICE() << "[ 1][t" << (0 <= thread_id && thread_id < 10 ? " " : "") << thread_id
+ << "] !!! Previous logging took " << total_time << " seconds !!!\n");
+ r_size.ignore();
}
}
-void FileLog::rotate() {
+void FileLog::after_rotation() {
if (path_.empty()) {
return;
}
- do_rotate();
+ do_after_rotation();
+}
+
+void FileLog::lazy_rotate() {
+ want_rotate_ = true;
}
-void FileLog::do_rotate() {
- auto current_verbosity_level = GET_VERBOSITY_LEVEL();
- SET_VERBOSITY_LEVEL(std::numeric_limits<int>::min()); // to ensure that nothing will be printed to the closed log
+void FileLog::do_after_rotation() {
+ want_rotate_ = false;
+ ScopedDisableLog disable_log; // to ensure that nothing will be printed to the closed log
CHECK(!path_.empty());
fd_.close();
- auto r_fd = FileFd::open(path_, FileFd::Create | FileFd::Truncate | FileFd::Write);
+ auto r_fd = FileFd::open(path_, FileFd::Create | FileFd::Write | FileFd::Append);
if (r_fd.is_error()) {
- process_fatal_error(r_fd.error().message());
+ process_fatal_error(PSLICE() << r_fd.error() << " in " << __FILE__ << " at " << __LINE__ << '\n');
}
fd_ = r_fd.move_as_ok();
- Fd::duplicate(fd_.get_fd(), Fd::Stderr()).ignore();
+ if (!Stderr().empty() && redirect_stderr_) {
+ fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
+ }
size_ = 0;
- SET_VERBOSITY_LEVEL(current_verbosity_level);
+}
+
+Result<unique_ptr<LogInterface>> FileLog::create(string path, int64 rotate_threshold, bool redirect_stderr) {
+ auto l = make_unique<FileLog>();
+ TRY_STATUS(l->init(std::move(path), rotate_threshold, redirect_stderr));
+ return std::move(l);
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.h
index 12e9d1479a..ad4ec5eb02 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FileLog.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,28 +10,45 @@
#include "td/utils/logging.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+
+#include <atomic>
namespace td {
-class FileLog : public LogInterface {
+class FileLog final : public LogInterface {
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
public:
- bool init(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD);
+ static Result<unique_ptr<LogInterface>> create(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD,
+ bool redirect_stderr = true);
+ Status init(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD, bool redirect_stderr = true);
+
+ Slice get_path() const;
+
+ vector<string> get_file_paths() final;
void set_rotate_threshold(int64 rotate_threshold);
- void append(CSlice cslice, int log_level) override;
+ int64 get_rotate_threshold() const;
- void rotate() override;
+ bool get_redirect_stderr() const;
+
+ void after_rotation() final;
+
+ void lazy_rotate();
private:
FileFd fd_;
string path_;
- int64 size_;
- int64 rotate_threshold_;
+ int64 size_ = 0;
+ int64 rotate_threshold_ = 0;
+ bool redirect_stderr_ = false;
+ std::atomic<bool> want_rotate_{false};
+
+ void do_append(int log_level, CSlice slice) final;
- void do_rotate();
+ void do_after_rotation();
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMap.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMap.h
new file mode 100644
index 0000000000..51aa6d3e4c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMap.h
@@ -0,0 +1,24 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+//#include "td/utils/FlatHashMapChunks.h"
+#include "td/utils/FlatHashTable.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/MapNode.h"
+
+#include <functional>
+//#include <unordered_map>
+
+namespace td {
+
+template <class KeyT, class ValueT, class HashT = Hash<KeyT>, class EqT = std::equal_to<KeyT>>
+using FlatHashMap = FlatHashTable<MapNode<KeyT, ValueT>, HashT, EqT>;
+//using FlatHashMap = FlatHashMapChunks<KeyT, ValueT, HashT, EqT>;
+//using FlatHashMap = std::unordered_map<KeyT, ValueT, HashT, EqT>;
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMapChunks.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMapChunks.h
new file mode 100644
index 0000000000..6df4842bc1
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashMapChunks.h
@@ -0,0 +1,575 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/bits.h"
+#include "td/utils/common.h"
+#include "td/utils/fixed_vector.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/MapNode.h"
+#include "td/utils/SetNode.h"
+
+#include <cstddef>
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <limits>
+#include <utility>
+
+#if defined(__SSE2__) || (TD_MSVC && (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2)))
+#define TD_SSE2 1
+#endif
+
+#ifdef __aarch64__
+#include <arm_neon.h>
+#endif
+
+#if TD_SSE2
+#include <emmintrin.h>
+#endif
+
+namespace td {
+template <int shift>
+struct MaskIterator {
+ uint64 mask;
+ explicit operator bool() const noexcept {
+ return mask != 0;
+ }
+ int pos() const {
+ return count_trailing_zeroes64(mask) / shift;
+ }
+ void next() {
+ mask &= mask - 1;
+ }
+
+ // For foreach
+ bool operator!=(MaskIterator &other) const {
+ return mask != other.mask;
+ }
+ auto operator*() const {
+ return pos();
+ }
+ void operator++() {
+ next();
+ }
+ auto begin() {
+ return *this;
+ }
+ auto end() {
+ return MaskIterator{0u};
+ }
+};
+
+struct MaskPortable {
+ static MaskIterator<1> equal_mask(uint8 *bytes, uint8 needle) {
+ uint64 res = 0;
+ for (int i = 0; i < 16; i++) {
+ res |= (bytes[i] == needle) << i;
+ }
+ return {res & ((1u << 14) - 1)};
+ }
+};
+
+#ifdef __aarch64__
+struct MaskNeonFolly {
+ static MaskIterator<4> equal_mask(uint8 *bytes, uint8 needle) {
+ uint8x16_t input_mask = vld1q_u8(bytes);
+ auto needle_mask = vdupq_n_u8(needle);
+ auto eq_mask = vceqq_u8(input_mask, needle_mask);
+ // get info from every byte into the bottom half of every uint16
+ // by shifting right 4, then round to get it into a 64-bit vector
+ uint8x8_t shifted_eq_mask = vshrn_n_u16(vreinterpretq_u16_u8(eq_mask), 4);
+ uint64 mask = vget_lane_u64(vreinterpret_u64_u8(shifted_eq_mask), 0);
+ return {mask & 0x11111111111111};
+ }
+};
+
+struct MaskNeon {
+ static MaskIterator<1> equal_mask(uint8 *bytes, uint8 needle) {
+ uint8x16_t input_mask = vld1q_u8(bytes);
+ auto needle_mask = vdupq_n_u8(needle);
+ auto eq_mask = vceqq_u8(input_mask, needle_mask);
+ uint16x8_t MASK = vdupq_n_u16(0x180);
+ uint16x8_t a_masked = vandq_u16(vreinterpretq_u16_u8(eq_mask), MASK);
+ const int16 __attribute__((aligned(16))) SHIFT_ARR[8] = {-7, -5, -3, -1, 1, 3, 5, 7};
+ int16x8_t SHIFT = vld1q_s16(SHIFT_ARR);
+ uint16x8_t a_shifted = vshlq_u16(a_masked, SHIFT);
+ return {vaddvq_u16(a_shifted) & ((1u << 14) - 1)};
+ }
+};
+#elif TD_SSE2
+struct MaskSse2 {
+ static MaskIterator<1> equal_mask(uint8 *bytes, uint8 needle) {
+ auto input_mask = _mm_loadu_si128(reinterpret_cast<const __m128i *>(bytes));
+ auto needle_mask = _mm_set1_epi8(needle);
+ auto match_mask = _mm_cmpeq_epi8(needle_mask, input_mask);
+ return {static_cast<uint32>(_mm_movemask_epi8(match_mask)) & ((1u << 14) - 1)};
+ }
+};
+#endif
+
+#ifdef __aarch64__
+using MaskHelper = MaskNeonFolly;
+#elif TD_SSE2
+using MaskHelper = MaskSse2;
+#else
+using MaskHelper = MaskPortable;
+#endif
+
+template <class NodeT, class HashT, class EqT>
+class FlatHashTableChunks {
+ public:
+ using Self = FlatHashTableChunks<NodeT, HashT, EqT>;
+ using Node = NodeT;
+ using NodeIterator = typename fixed_vector<Node>::iterator;
+ using ConstNodeIterator = typename fixed_vector<Node>::const_iterator;
+
+ using KeyT = typename Node::public_key_type;
+ using key_type = typename Node::public_key_type;
+ using value_type = typename Node::public_type;
+
+ struct Iterator {
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = FlatHashTableChunks::value_type;
+ using pointer = value_type *;
+ using reference = value_type &;
+
+ friend class FlatHashTableChunks;
+ Iterator &operator++() {
+ do {
+ ++it_;
+ } while (it_ != map_->nodes_.end() && it_->empty());
+ return *this;
+ }
+ Iterator &operator--() {
+ do {
+ --it_;
+ } while (it_->empty());
+ return *this;
+ }
+ reference operator*() {
+ return it_->get_public();
+ }
+ pointer operator->() {
+ return &it_->get_public();
+ }
+ bool operator==(const Iterator &other) const {
+ DCHECK(map_ == other.map_);
+ return it_ == other.it_;
+ }
+ bool operator!=(const Iterator &other) const {
+ DCHECK(map_ == other.map_);
+ return it_ != other.it_;
+ }
+
+ Iterator() = default;
+ Iterator(NodeIterator it, Self *map) : it_(std::move(it)), map_(map) {
+ }
+
+ private:
+ NodeIterator it_;
+ Self *map_;
+ };
+
+ struct ConstIterator {
+ using iterator_category = std::bidirectional_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = FlatHashTableChunks::value_type;
+ using pointer = const value_type *;
+ using reference = const value_type &;
+
+ friend class FlatHashTableChunks;
+ ConstIterator &operator++() {
+ ++it_;
+ return *this;
+ }
+ ConstIterator &operator--() {
+ --it_;
+ return *this;
+ }
+ reference operator*() {
+ return *it_;
+ }
+ pointer operator->() {
+ return &*it_;
+ }
+ bool operator==(const ConstIterator &other) const {
+ return it_ == other.it_;
+ }
+ bool operator!=(const ConstIterator &other) const {
+ return it_ != other.it_;
+ }
+
+ ConstIterator() = default;
+ ConstIterator(Iterator it) : it_(std::move(it)) {
+ }
+
+ private:
+ Iterator it_;
+ };
+ using iterator = Iterator;
+ using const_iterator = ConstIterator;
+
+ FlatHashTableChunks() = default;
+ FlatHashTableChunks(const FlatHashTableChunks &other) {
+ assign(other);
+ }
+ FlatHashTableChunks &operator=(const FlatHashTableChunks &other) {
+ clear();
+ assign(other);
+ return *this;
+ }
+
+ FlatHashTableChunks(std::initializer_list<Node> nodes) {
+ reserve(nodes.size());
+ for (auto &new_node : nodes) {
+ CHECK(!new_node.empty());
+ if (count(new_node.key()) > 0) {
+ continue;
+ }
+ Node node;
+ node.copy_from(new_node);
+ emplace_node(std::move(node));
+ }
+ }
+
+ FlatHashTableChunks(FlatHashTableChunks &&other) noexcept {
+ swap(other);
+ }
+ FlatHashTableChunks &operator=(FlatHashTableChunks &&other) noexcept {
+ swap(other);
+ return *this;
+ }
+ void swap(FlatHashTableChunks &other) noexcept {
+ nodes_.swap(other.nodes_);
+ chunks_.swap(other.chunks_);
+ std::swap(used_nodes_, other.used_nodes_);
+ }
+ ~FlatHashTableChunks() = default;
+
+ size_t bucket_count() const {
+ return nodes_.size();
+ }
+
+ Iterator find(const KeyT &key) {
+ if (empty() || is_hash_table_key_empty(key)) {
+ return end();
+ }
+ const auto hash = calc_hash(key);
+ auto chunk_it = get_chunk_it(hash.chunk_i);
+ while (true) {
+ auto chunk_i = chunk_it.pos();
+ auto chunk_begin = nodes_.begin() + chunk_i * Chunk::CHUNK_SIZE;
+ //__builtin_prefetch(chunk_begin);
+ auto &chunk = chunks_[chunk_i];
+ auto mask_it = MaskHelper::equal_mask(chunk.ctrl, hash.small_hash);
+ for (auto pos : mask_it) {
+ auto it = chunk_begin + pos;
+ if (likely(EqT()(it->key(), key))) {
+ return Iterator{it, this};
+ }
+ }
+ if (chunk.skipped_cnt == 0) {
+ break;
+ }
+ chunk_it.next();
+ }
+ return end();
+ }
+
+ ConstIterator find(const KeyT &key) const {
+ return ConstIterator(const_cast<Self *>(this)->find(key));
+ }
+
+ size_t size() const {
+ return used_nodes_;
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+
+ Iterator begin() {
+ if (empty()) {
+ return end();
+ }
+ auto it = nodes_.begin();
+ while (it->empty()) {
+ ++it;
+ }
+ return Iterator(it, this);
+ }
+ Iterator end() {
+ return Iterator(nodes_.end(), this);
+ }
+
+ ConstIterator begin() const {
+ return ConstIterator(const_cast<Self *>(this)->begin());
+ }
+ ConstIterator end() const {
+ return ConstIterator(const_cast<Self *>(this)->end());
+ }
+
+ void reserve(size_t size) {
+ //size_t want_size = normalize(size * 5 / 3 + 1);
+ size_t want_size = normalize(size * 14 / 12 + 1);
+ // size_t want_size = size * 2;
+ if (want_size > nodes_.size()) {
+ resize(want_size);
+ }
+ }
+
+ template <class... ArgsT>
+ std::pair<Iterator, bool> emplace(KeyT key, ArgsT &&...args) {
+ CHECK(!is_hash_table_key_empty(key));
+ auto it = find(key);
+ if (it != end()) {
+ return {it, false};
+ }
+ try_grow();
+
+ auto hash = calc_hash(key);
+ auto chunk_it = get_chunk_it(hash.chunk_i);
+ while (true) {
+ auto chunk_i = chunk_it.pos();
+ auto &chunk = chunks_[chunk_i];
+ auto mask_it = MaskHelper::equal_mask(chunk.ctrl, 0);
+ if (mask_it) {
+ auto shift = mask_it.pos();
+ DCHECK(chunk.ctrl[shift] == 0);
+ auto node_it = nodes_.begin() + shift + chunk_i * Chunk::CHUNK_SIZE;
+ DCHECK(node_it->empty());
+ node_it->emplace(std::move(key), std::forward<ArgsT>(args)...);
+ DCHECK(!node_it->empty());
+ chunk.ctrl[shift] = hash.small_hash;
+ used_nodes_++;
+ return {{node_it, this}, true};
+ }
+ CHECK(chunk.skipped_cnt != std::numeric_limits<uint16>::max());
+ chunk.skipped_cnt++;
+ chunk_it.next();
+ }
+ }
+
+ std::pair<Iterator, bool> insert(KeyT key) {
+ return emplace(std::move(key));
+ }
+
+ template <class ItT>
+ void insert(ItT begin, ItT end) {
+ for (; begin != end; ++begin) {
+ emplace(*begin);
+ }
+ }
+
+ template <class T = typename Node::second_type>
+ T &operator[](const KeyT &key) {
+ return emplace(key).first->second;
+ }
+
+ size_t erase(const KeyT &key) {
+ auto it = find(key);
+ if (it == end()) {
+ return 0;
+ }
+ erase(it);
+ try_shrink();
+ return 1;
+ }
+
+ size_t count(const KeyT &key) const {
+ return find(key) != end();
+ }
+
+ void clear() {
+ used_nodes_ = 0;
+ nodes_ = {};
+ chunks_ = {};
+ }
+
+ void erase(Iterator it) {
+ DCHECK(it != end());
+ DCHECK(!it.it_->empty());
+ erase_node(it.it_);
+ }
+
+ template <class F>
+ void remove_if(F &&f) {
+ for (auto it = nodes_.begin(), end = nodes_.end(); it != end; ++it) {
+ if (!it->empty() && f(it->get_public())) {
+ erase_node(it);
+ }
+ }
+ try_shrink();
+ }
+
+ private:
+ struct Chunk {
+ static constexpr int CHUNK_SIZE = 14;
+ static constexpr int MASK = (1 << CHUNK_SIZE) - 1;
+ // 0x0 - empty
+ uint8 ctrl[CHUNK_SIZE] = {};
+ uint16 skipped_cnt{0};
+ };
+ fixed_vector<Node> nodes_;
+ fixed_vector<Chunk> chunks_;
+ size_t used_nodes_{};
+
+ void assign(const FlatHashTableChunks &other) {
+ reserve(other.size());
+ for (const auto &new_node : other) {
+ Node node;
+ node.copy_from(new_node);
+ emplace_node(std::move(node));
+ }
+ }
+
+ void try_grow() {
+ if (should_grow(used_nodes_ + 1, nodes_.size())) {
+ grow();
+ }
+ }
+ static bool should_grow(size_t used_count, size_t bucket_count) {
+ return used_count * 14 > bucket_count * 12;
+ }
+ void try_shrink() {
+ if (should_shrink(used_nodes_, nodes_.size())) {
+ shrink();
+ }
+ }
+ static bool should_shrink(size_t used_count, size_t bucket_count) {
+ return used_count * 10 < bucket_count;
+ }
+
+ static size_t normalize(size_t size) {
+ auto x = (size / Chunk::CHUNK_SIZE) | 1;
+ auto y = static_cast<size_t>(1) << (64 - count_leading_zeroes64(x));
+ return y * Chunk::CHUNK_SIZE;
+ }
+
+ void shrink() {
+ size_t want_size = normalize((used_nodes_ + 1) * 5 / 3 + 1);
+ resize(want_size);
+ }
+
+ void grow() {
+ size_t want_size = normalize(2 * nodes_.size() - !nodes_.empty());
+ resize(want_size);
+ }
+
+ struct HashInfo {
+ size_t chunk_i;
+ uint8 small_hash;
+ };
+ struct ChunkIt {
+ size_t chunk_i;
+ size_t chunk_mask;
+ size_t shift;
+
+ size_t pos() const {
+ return chunk_i;
+ }
+ void next() {
+ DCHECK((chunk_mask & (chunk_mask + 1)) == 0);
+ shift++;
+ chunk_i += shift;
+ chunk_i &= chunk_mask;
+ }
+ };
+
+ ChunkIt get_chunk_it(size_t chunk_i) {
+ return ChunkIt{chunk_i, chunks_.size() - 1, 0};
+ }
+
+ HashInfo calc_hash(const KeyT &key) {
+ auto h = HashT()(key);
+ return {(h >> 8) % chunks_.size(), static_cast<uint8>(0x80 | h)};
+ }
+
+ void resize(size_t new_size) {
+ CHECK(new_size >= Chunk::CHUNK_SIZE);
+ fixed_vector<Node> old_nodes(new_size);
+ fixed_vector<Chunk> chunks(new_size / Chunk::CHUNK_SIZE);
+ old_nodes.swap(nodes_);
+ chunks_ = std::move(chunks);
+ used_nodes_ = 0;
+
+ for (auto &node : old_nodes) {
+ if (node.empty()) {
+ continue;
+ }
+ emplace_node(std::move(node));
+ }
+ }
+
+ void emplace_node(Node &&node) {
+ DCHECK(!node.empty());
+ auto hash = calc_hash(node.key());
+ auto chunk_it = get_chunk_it(hash.chunk_i);
+ while (true) {
+ auto chunk_i = chunk_it.pos();
+ auto &chunk = chunks_[chunk_i];
+ auto mask_it = MaskHelper::equal_mask(chunk.ctrl, 0);
+ if (mask_it) {
+ auto shift = mask_it.pos();
+ auto node_it = nodes_.begin() + shift + chunk_i * Chunk::CHUNK_SIZE;
+ DCHECK(node_it->empty());
+ *node_it = std::move(node);
+ DCHECK(chunk.ctrl[shift] == 0);
+ chunk.ctrl[shift] = hash.small_hash;
+ DCHECK(chunk.ctrl[shift] != 0);
+ used_nodes_++;
+ break;
+ }
+ CHECK(chunk.skipped_cnt != std::numeric_limits<uint16>::max());
+ chunk.skipped_cnt++;
+ chunk_it.next();
+ }
+ }
+
+ void next_bucket(size_t &bucket) const {
+ bucket++;
+ if (unlikely(bucket == nodes_.size())) {
+ bucket = 0;
+ }
+ }
+
+ void erase_node(NodeIterator it) {
+ DCHECK(!it->empty());
+ size_t empty_i = it - nodes_.begin();
+ DCHECK(0 <= empty_i && empty_i < nodes_.size());
+ auto empty_chunk_i = empty_i / Chunk::CHUNK_SIZE;
+ auto hash = calc_hash(it->key());
+ auto chunk_it = get_chunk_it(hash.chunk_i);
+ while (true) {
+ auto chunk_i = chunk_it.pos();
+ auto &chunk = chunks_[chunk_i];
+ if (chunk_i == empty_chunk_i) {
+ chunk.ctrl[empty_i - empty_chunk_i * Chunk::CHUNK_SIZE] = 0;
+ break;
+ }
+ chunk.skipped_cnt--;
+ chunk_it.next();
+ }
+ it->clear();
+ used_nodes_--;
+ }
+};
+
+template <class KeyT, class ValueT, class HashT = Hash<KeyT>, class EqT = std::equal_to<KeyT>>
+using FlatHashMapChunks = FlatHashTableChunks<MapNode<KeyT, ValueT>, HashT, EqT>;
+
+template <class KeyT, class HashT = Hash<KeyT>, class EqT = std::equal_to<KeyT>>
+using FlatHashSetChunks = FlatHashTableChunks<SetNode<KeyT>, HashT, EqT>;
+
+template <class NodeT, class HashT, class EqT, class FuncT>
+void table_remove_if(FlatHashTableChunks<NodeT, HashT, EqT> &table, FuncT &&func) {
+ table.remove_if(func);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashSet.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashSet.h
new file mode 100644
index 0000000000..385485979a
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashSet.h
@@ -0,0 +1,24 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+//#include "td/utils/FlatHashMapChunks.h"
+#include "td/utils/FlatHashTable.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/SetNode.h"
+
+#include <functional>
+//#include <unordered_set>
+
+namespace td {
+
+template <class KeyT, class HashT = Hash<KeyT>, class EqT = std::equal_to<KeyT>>
+using FlatHashSet = FlatHashTable<SetNode<KeyT>, HashT, EqT>;
+//using FlatHashSet = FlatHashSetChunks<KeyT, HashT, EqT>;
+//using FlatHashSet = std::unordered_set<KeyT, HashT, EqT>;
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.cpp
new file mode 100644
index 0000000000..abe7afa764
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.cpp
@@ -0,0 +1,24 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/FlatHashTable.h"
+
+#include "td/utils/bits.h"
+#include "td/utils/Random.h"
+
+namespace td {
+namespace detail {
+
+uint32 normalize_flat_hash_table_size(uint32 size) {
+ return td::max(static_cast<uint32>(1) << (32 - count_leading_zeroes32(size)), static_cast<uint32>(8));
+}
+
+uint32 get_random_flat_hash_table_bucket(uint32 bucket_count_mask) {
+ return Random::fast_uint32() & bucket_count_mask;
+}
+
+} // namespace detail
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.h
new file mode 100644
index 0000000000..a312ad9533
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FlatHashTable.h
@@ -0,0 +1,551 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/HashTableUtils.h"
+
+#include <cstddef>
+#include <initializer_list>
+#include <iterator>
+#include <utility>
+
+namespace td {
+
+namespace detail {
+uint32 normalize_flat_hash_table_size(uint32 size);
+uint32 get_random_flat_hash_table_bucket(uint32 bucket_count_mask);
+} // namespace detail
+
+template <class NodeT, class HashT, class EqT>
+class FlatHashTable {
+ static constexpr uint32 INVALID_BUCKET = 0xFFFFFFFF;
+
+ void allocate_nodes(uint32 size) {
+ DCHECK(size >= 8);
+ DCHECK((size & (size - 1)) == 0);
+ CHECK(size <= min(static_cast<uint32>(1) << 29, static_cast<uint32>(0x7FFFFFFF / sizeof(NodeT))));
+ nodes_ = new NodeT[size];
+ // used_node_count_ = 0;
+ bucket_count_mask_ = size - 1;
+ bucket_count_ = size;
+ begin_bucket_ = INVALID_BUCKET;
+ }
+
+ static void clear_nodes(NodeT *nodes) {
+ delete[] nodes;
+ }
+
+ public:
+ using KeyT = typename NodeT::public_key_type;
+ using key_type = typename NodeT::public_key_type;
+ using value_type = typename NodeT::public_type;
+
+ // TODO use EndSentinel for end() after switching to C++17
+ // struct EndSentinel {};
+
+ struct Iterator {
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = FlatHashTable::value_type;
+ using pointer = value_type *;
+ using reference = value_type &;
+
+ Iterator &operator++() {
+ DCHECK(it_ != nullptr);
+ do {
+ if (unlikely(++it_ == end_)) {
+ it_ = begin_;
+ }
+ if (unlikely(it_ == start_)) {
+ it_ = nullptr;
+ break;
+ }
+ } while (it_->empty());
+ return *this;
+ }
+ reference operator*() {
+ return it_->get_public();
+ }
+ const value_type &operator*() const {
+ return it_->get_public();
+ }
+ pointer operator->() {
+ return &it_->get_public();
+ }
+ const value_type *operator->() const {
+ return &it_->get_public();
+ }
+
+ NodeT *get() {
+ return it_;
+ }
+
+ bool operator==(const Iterator &other) const {
+ DCHECK(other.it_ == nullptr);
+ return it_ == nullptr;
+ }
+ bool operator!=(const Iterator &other) const {
+ DCHECK(other.it_ == nullptr);
+ return it_ != nullptr;
+ }
+
+ Iterator() = default;
+ Iterator(NodeT *it, NodeT *begin, NodeT *end) : it_(it), begin_(begin), start_(it), end_(end) {
+ }
+
+ private:
+ NodeT *it_ = nullptr;
+ NodeT *begin_ = nullptr;
+ NodeT *start_ = nullptr;
+ NodeT *end_ = nullptr;
+ };
+
+ struct ConstIterator {
+ using iterator_category = std::forward_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using value_type = FlatHashTable::value_type;
+ using pointer = const value_type *;
+ using reference = const value_type &;
+
+ ConstIterator &operator++() {
+ ++it_;
+ return *this;
+ }
+ reference operator*() const {
+ return *it_;
+ }
+ pointer operator->() const {
+ return &*it_;
+ }
+ bool operator==(const ConstIterator &other) const {
+ return it_ == other.it_;
+ }
+ bool operator!=(const ConstIterator &other) const {
+ return it_ != other.it_;
+ }
+
+ ConstIterator() = default;
+ ConstIterator(Iterator it) : it_(std::move(it)) {
+ }
+
+ private:
+ Iterator it_;
+ };
+ using iterator = Iterator;
+ using const_iterator = ConstIterator;
+
+ struct NodePointer {
+ value_type &operator*() {
+ return it_->get_public();
+ }
+ const value_type &operator*() const {
+ return it_->get_public();
+ }
+ value_type *operator->() {
+ return &it_->get_public();
+ }
+ const value_type *operator->() const {
+ return &it_->get_public();
+ }
+
+ NodeT *get() {
+ return it_;
+ }
+
+ bool operator==(const Iterator &) const {
+ return it_ == nullptr;
+ }
+ bool operator!=(const Iterator &) const {
+ return it_ != nullptr;
+ }
+
+ explicit NodePointer(NodeT *it) : it_(it) {
+ }
+
+ private:
+ NodeT *it_ = nullptr;
+ };
+
+ struct ConstNodePointer {
+ const value_type &operator*() const {
+ return it_->get_public();
+ }
+ const value_type *operator->() const {
+ return &it_->get_public();
+ }
+
+ bool operator==(const ConstIterator &) const {
+ return it_ == nullptr;
+ }
+ bool operator!=(const ConstIterator &) const {
+ return it_ != nullptr;
+ }
+
+ const NodeT *get() const {
+ return it_;
+ }
+
+ explicit ConstNodePointer(const NodeT *it) : it_(it) {
+ }
+
+ private:
+ const NodeT *it_ = nullptr;
+ };
+
+ FlatHashTable() = default;
+ FlatHashTable(const FlatHashTable &other) = delete;
+ FlatHashTable &operator=(const FlatHashTable &other) = delete;
+
+ FlatHashTable(std::initializer_list<NodeT> nodes) {
+ if (nodes.size() == 0) {
+ return;
+ }
+ reserve(nodes.size());
+ uint32 used_nodes = 0;
+ for (auto &new_node : nodes) {
+ CHECK(!new_node.empty());
+ auto bucket = calc_bucket(new_node.key());
+ while (true) {
+ auto &node = nodes_[bucket];
+ if (node.empty()) {
+ node.copy_from(new_node);
+ used_nodes++;
+ break;
+ }
+ if (EqT()(node.key(), new_node.key())) {
+ break;
+ }
+ next_bucket(bucket);
+ }
+ }
+ used_node_count_ = used_nodes;
+ }
+
+ FlatHashTable(FlatHashTable &&other) noexcept
+ : nodes_(other.nodes_)
+ , used_node_count_(other.used_node_count_)
+ , bucket_count_mask_(other.bucket_count_mask_)
+ , bucket_count_(other.bucket_count_)
+ , begin_bucket_(other.begin_bucket_) {
+ other.drop();
+ }
+ void operator=(FlatHashTable &&other) noexcept {
+ clear();
+ nodes_ = other.nodes_;
+ used_node_count_ = other.used_node_count_;
+ bucket_count_mask_ = other.bucket_count_mask_;
+ bucket_count_ = other.bucket_count_;
+ begin_bucket_ = other.begin_bucket_;
+ other.drop();
+ }
+ ~FlatHashTable() {
+ clear_nodes(nodes_);
+ }
+
+ void swap(FlatHashTable &other) noexcept {
+ std::swap(nodes_, other.nodes_);
+ std::swap(used_node_count_, other.used_node_count_);
+ std::swap(bucket_count_mask_, other.bucket_count_mask_);
+ std::swap(bucket_count_, other.bucket_count_);
+ std::swap(begin_bucket_, other.begin_bucket_);
+ }
+
+ uint32 bucket_count() const {
+ return bucket_count_;
+ }
+
+ NodePointer find(const KeyT &key) {
+ return NodePointer(find_impl(key));
+ }
+
+ ConstNodePointer find(const KeyT &key) const {
+ return ConstNodePointer(const_cast<FlatHashTable *>(this)->find_impl(key));
+ }
+
+ size_t size() const {
+ return used_node_count_;
+ }
+
+ bool empty() const {
+ return used_node_count_ == 0;
+ }
+
+ Iterator begin() {
+ return create_iterator(begin_impl());
+ }
+ Iterator end() {
+ return Iterator();
+ }
+ ConstIterator begin() const {
+ return ConstIterator(const_cast<FlatHashTable *>(this)->begin());
+ }
+ ConstIterator end() const {
+ return ConstIterator();
+ }
+
+ void reserve(size_t size) {
+ if (size == 0) {
+ return;
+ }
+ CHECK(size <= (1u << 29));
+ uint32 want_size = detail::normalize_flat_hash_table_size(static_cast<uint32>(size) * 5 / 3 + 1);
+ if (want_size > bucket_count()) {
+ resize(want_size);
+ }
+ }
+
+ template <class... ArgsT>
+ std::pair<NodePointer, bool> emplace(KeyT key, ArgsT &&...args) {
+ CHECK(!is_hash_table_key_empty(key));
+ if (unlikely(bucket_count_mask_ == 0)) {
+ CHECK(used_node_count_ == 0);
+ resize(8);
+ }
+ auto bucket = calc_bucket(key);
+ while (true) {
+ auto &node = nodes_[bucket];
+ if (node.empty()) {
+ if (unlikely(used_node_count_ * 5 >= bucket_count_mask_ * 3)) {
+ resize(2 * bucket_count_);
+ CHECK(used_node_count_ * 5 < bucket_count_mask_ * 3);
+ return emplace(std::move(key), std::forward<ArgsT>(args)...);
+ }
+ invalidate_iterators();
+
+ node.emplace(std::move(key), std::forward<ArgsT>(args)...);
+ used_node_count_++;
+ return {NodePointer(&node), true};
+ }
+ if (EqT()(node.key(), key)) {
+ return {NodePointer(&node), false};
+ }
+ next_bucket(bucket);
+ }
+ }
+
+ std::pair<NodePointer, bool> insert(KeyT key) {
+ return emplace(std::move(key));
+ }
+
+ template <class ItT>
+ void insert(ItT begin, ItT end) {
+ for (; begin != end; ++begin) {
+ emplace(*begin);
+ }
+ }
+
+ template <class T = typename NodeT::second_type>
+ T &operator[](const KeyT &key) {
+ return emplace(key).first->second;
+ }
+
+ size_t erase(const KeyT &key) {
+ auto *node = find_impl(key);
+ if (node == nullptr) {
+ return 0;
+ }
+ erase_node(node);
+ try_shrink();
+ return 1;
+ }
+
+ size_t count(const KeyT &key) const {
+ return const_cast<FlatHashTable *>(this)->find_impl(key) != nullptr;
+ }
+
+ void clear() {
+ if (nodes_ != nullptr) {
+ clear_nodes(nodes_);
+ drop();
+ }
+ }
+
+ void erase(Iterator it) {
+ DCHECK(it != end());
+ erase_node(it.get());
+ try_shrink();
+ }
+
+ void erase(NodePointer it) {
+ DCHECK(it != end());
+ erase_node(it.get());
+ try_shrink();
+ }
+
+ template <class F>
+ void remove_if(F &&f) {
+ if (empty()) {
+ return;
+ }
+
+ auto it = begin_impl();
+ auto end = nodes_ + bucket_count();
+ while (it != end && !it->empty()) {
+ ++it;
+ }
+ if (it == end) {
+ do {
+ --it;
+ } while (!it->empty());
+ }
+ auto first_empty = it;
+ while (it != end) {
+ if (!it->empty() && f(it->get_public())) {
+ erase_node(it);
+ } else {
+ ++it;
+ }
+ }
+ for (it = nodes_; it != first_empty;) {
+ if (!it->empty() && f(it->get_public())) {
+ erase_node(it);
+ } else {
+ ++it;
+ }
+ }
+ try_shrink();
+ }
+
+ private:
+ NodeT *nodes_ = nullptr;
+ uint32 used_node_count_ = 0;
+ uint32 bucket_count_mask_ = 0;
+ uint32 bucket_count_ = 0;
+ uint32 begin_bucket_ = 0;
+
+ void drop() {
+ nodes_ = nullptr;
+ used_node_count_ = 0;
+ bucket_count_mask_ = 0;
+ bucket_count_ = 0;
+ begin_bucket_ = 0;
+ }
+
+ NodeT *begin_impl() {
+ if (empty()) {
+ return nullptr;
+ }
+ if (begin_bucket_ == INVALID_BUCKET) {
+ begin_bucket_ = detail::get_random_flat_hash_table_bucket(bucket_count_mask_);
+ while (nodes_[begin_bucket_].empty()) {
+ next_bucket(begin_bucket_);
+ }
+ }
+ return nodes_ + begin_bucket_;
+ }
+
+ NodeT *find_impl(const KeyT &key) {
+ if (unlikely(nodes_ == nullptr) || is_hash_table_key_empty(key)) {
+ return nullptr;
+ }
+ auto bucket = calc_bucket(key);
+ while (true) {
+ auto &node = nodes_[bucket];
+ if (node.empty()) {
+ return nullptr;
+ }
+ if (EqT()(node.key(), key)) {
+ return &node;
+ }
+ next_bucket(bucket);
+ }
+ }
+
+ void try_shrink() {
+ DCHECK(nodes_ != nullptr);
+ if (unlikely(used_node_count_ * 10 < bucket_count_mask_ && bucket_count_mask_ > 7)) {
+ resize(detail::normalize_flat_hash_table_size((used_node_count_ + 1) * 5 / 3 + 1));
+ }
+ invalidate_iterators();
+ }
+
+ uint32 calc_bucket(const KeyT &key) const {
+ return HashT()(key) & bucket_count_mask_;
+ }
+
+ inline void next_bucket(uint32 &bucket) const {
+ bucket = (bucket + 1) & bucket_count_mask_;
+ }
+
+ void resize(uint32 new_size) {
+ if (unlikely(nodes_ == nullptr)) {
+ allocate_nodes(new_size);
+ used_node_count_ = 0;
+ return;
+ }
+
+ auto old_nodes = nodes_;
+ uint32 old_size = used_node_count_;
+ uint32 old_bucket_count = bucket_count_;
+ allocate_nodes(new_size);
+ used_node_count_ = old_size;
+
+ auto old_nodes_end = old_nodes + old_bucket_count;
+ for (NodeT *old_node = old_nodes; old_node != old_nodes_end; ++old_node) {
+ if (old_node->empty()) {
+ continue;
+ }
+ auto bucket = calc_bucket(old_node->key());
+ while (!nodes_[bucket].empty()) {
+ next_bucket(bucket);
+ }
+ nodes_[bucket] = std::move(*old_node);
+ }
+ clear_nodes(old_nodes);
+ }
+
+ void erase_node(NodeT *it) {
+ DCHECK(nodes_ <= it && static_cast<size_t>(it - nodes_) < bucket_count());
+ it->clear();
+ used_node_count_--;
+
+ const auto bucket_count = bucket_count_;
+ const auto *end = nodes_ + bucket_count;
+ for (auto *test_node = it + 1; test_node != end; test_node++) {
+ if (likely(test_node->empty())) {
+ return;
+ }
+
+ auto want_node = nodes_ + calc_bucket(test_node->key());
+ if (want_node <= it || want_node > test_node) {
+ *it = std::move(*test_node);
+ it = test_node;
+ }
+ }
+
+ auto empty_i = static_cast<uint32>(it - nodes_);
+ auto empty_bucket = empty_i;
+ for (uint32 test_i = bucket_count;; test_i++) {
+ auto test_bucket = test_i - bucket_count_;
+ if (nodes_[test_bucket].empty()) {
+ return;
+ }
+
+ auto want_i = calc_bucket(nodes_[test_bucket].key());
+ if (want_i < empty_i) {
+ want_i += bucket_count;
+ }
+
+ if (want_i <= empty_i || want_i > test_i) {
+ nodes_[empty_bucket] = std::move(nodes_[test_bucket]);
+ empty_i = test_i;
+ empty_bucket = test_bucket;
+ }
+ }
+ }
+
+ Iterator create_iterator(NodeT *node) {
+ return Iterator(node, nodes_, nodes_ + bucket_count());
+ }
+
+ void invalidate_iterators() {
+ begin_bucket_ = INVALID_BUCKET;
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlFast.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlFast.h
index 9f047881aa..e5570f6d2a 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlFast.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlFast.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,56 +7,81 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/TimedStat.h"
namespace td {
class FloodControlFast {
public:
- uint32 add_event(int32 now) {
- for (auto &limit : limits_) {
- limit.stat_.add_event(CounterStat::Event(), now);
- if (limit.stat_.get_stat(now).count_ > limit.count_) {
- wakeup_at_ = max(wakeup_at_, now + limit.duration_ * 2);
- }
+ void add_event(double now) {
+ for (auto &bucket : buckets_) {
+ bucket.add_event(now);
+ wakeup_at_ = td::max(wakeup_at_, bucket.get_wakeup_at());
}
- return wakeup_at_;
}
- uint32 get_wakeup_at() {
+
+ double get_wakeup_at() const {
return wakeup_at_;
}
- void add_limit(uint32 duration, int32 count) {
- limits_.push_back({TimedStat<CounterStat>(duration, 0), duration, count});
+ void add_limit(double duration, double count) {
+ buckets_.emplace_back(duration, count);
}
void clear_events() {
- for (auto &limit : limits_) {
- limit.stat_.clear_events();
+ for (auto &bucket : buckets_) {
+ bucket.clear_events();
}
wakeup_at_ = 0;
}
private:
- class CounterStat {
+ class FloodControlBucket {
public:
- struct Event {};
- int32 count_ = 0;
- void on_event(Event e) {
- count_++;
+ FloodControlBucket(double duration, double count)
+ : max_capacity_(count - 1), speed_(count / duration), volume_(max_capacity_) {
}
- void clear() {
- count_ = 0;
+
+ void add_event(double now, double size = 1) {
+ CHECK(now >= wakeup_at_);
+ update_volume(now);
+ if (volume_ >= size) {
+ volume_ -= size;
+ return;
+ }
+ size -= volume_;
+ volume_ = 0;
+ wakeup_at_ = volume_at_ + size / speed_;
+ volume_at_ = wakeup_at_;
}
- };
- uint32 wakeup_at_ = 0;
- struct Limit {
- TimedStat<CounterStat> stat_;
- uint32 duration_;
- int32 count_;
+ double get_wakeup_at() const {
+ return wakeup_at_;
+ }
+
+ void clear_events() {
+ volume_ = max_capacity_;
+ volume_at_ = 0;
+ wakeup_at_ = 0;
+ }
+
+ private:
+ const double max_capacity_{1};
+ const double speed_{1};
+ double volume_{1};
+
+ double volume_at_{0};
+ double wakeup_at_{0};
+
+ void update_volume(double now) {
+ CHECK(now >= volume_at_);
+ auto passed = now - volume_at_;
+ volume_ = td::min(volume_ + passed * speed_, max_capacity_);
+ volume_at_ = now;
+ }
};
- std::vector<Limit> limits_;
+
+ double wakeup_at_ = 0;
+ vector<FloodControlBucket> buckets_;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.cpp
new file mode 100644
index 0000000000..98709c6b96
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.cpp
@@ -0,0 +1,32 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/FloodControlGlobal.h"
+
+namespace td {
+
+FloodControlGlobal::FloodControlGlobal(uint64 limit) : limit_(limit) {
+}
+
+void FloodControlGlobal::finish() {
+ auto old_value = active_count_.fetch_sub(1, std::memory_order_relaxed);
+ CHECK(old_value > 0);
+}
+
+FloodControlGlobal::Guard FloodControlGlobal::try_start() {
+ auto old_value = active_count_.fetch_add(1, std::memory_order_relaxed);
+ if (old_value >= limit_) {
+ finish();
+ return nullptr;
+ }
+ return Guard(this);
+}
+
+void FloodControlGlobal::Finish::operator()(FloodControlGlobal *ctrl) const {
+ ctrl->finish();
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.h
new file mode 100644
index 0000000000..e4f318867b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlGlobal.h
@@ -0,0 +1,35 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#include <atomic>
+#include <memory>
+
+namespace td {
+
+// Restricts the total number of events
+class FloodControlGlobal {
+ public:
+ explicit FloodControlGlobal(uint64 limit);
+
+ struct Finish {
+ void operator()(FloodControlGlobal *ctrl) const;
+ };
+ using Guard = std::unique_ptr<FloodControlGlobal, Finish>;
+
+ Guard try_start();
+
+ private:
+ std::atomic<uint64> active_count_{0};
+ uint64 limit_{0};
+
+ void finish();
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlStrict.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlStrict.h
index 521fbbedc0..42894cdf7f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlStrict.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/FloodControlStrict.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,6 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include <limits>
@@ -17,22 +16,23 @@ namespace td {
// Should be just fine for small counters.
class FloodControlStrict {
public:
- int32 add_event(int32 now) {
+ // there is no reason to return wakeup_at_, because it will be a time before the next allowed event, not current
+ void add_event(double now) {
events_.push_back(Event{now});
if (without_update_ > 0) {
without_update_--;
} else {
update(now);
}
- return wakeup_at_;
}
- // no more than count in each duration.
- void add_limit(int32 duration, int32 count) {
+ // no more than count in each duration
+ void add_limit(int32 duration, size_t count) {
limits_.push_back(Limit{duration, count, 0});
+ without_update_ = 0;
}
- int32 get_wakeup_at() {
+ double get_wakeup_at() const {
return wakeup_at_;
}
@@ -42,20 +42,22 @@ class FloodControlStrict {
limit.pos_ = 0;
}
without_update_ = 0;
- wakeup_at_ = 0;
+ wakeup_at_ = 0.0;
}
- int32 update(int32 now) {
+ private:
+ void update(double now) {
size_t min_pos = events_.size();
without_update_ = std::numeric_limits<size_t>::max();
for (auto &limit : limits_) {
- if (limit.pos_ + limit.count_ < events_.size()) {
+ if (limit.count_ < events_.size() - limit.pos_) {
limit.pos_ = events_.size() - limit.count_;
}
// binary-search? :D
- while (limit.pos_ < events_.size() && events_[limit.pos_].timestamp_ + limit.duration_ < now) {
+ auto end_time = now - limit.duration_;
+ while (limit.pos_ < events_.size() && events_[limit.pos_].timestamp_ < end_time) {
limit.pos_++;
}
@@ -64,7 +66,7 @@ class FloodControlStrict {
wakeup_at_ = max(wakeup_at_, events_[limit.pos_].timestamp_ + limit.duration_);
without_update_ = 0;
} else {
- without_update_ = min(without_update_, limit.count_ + limit.pos_ - events_.size());
+ without_update_ = min(without_update_, limit.count_ + limit.pos_ - events_.size() - 1);
}
min_pos = min(min_pos, limit.pos_);
@@ -76,22 +78,20 @@ class FloodControlStrict {
}
events_.erase(events_.begin(), events_.begin() + min_pos);
}
- return wakeup_at_;
}
- private:
- int32 wakeup_at_ = 0;
+ double wakeup_at_ = 0.0;
struct Event {
- int32 timestamp_;
+ double timestamp_;
};
struct Limit {
int32 duration_;
- int32 count_;
+ size_t count_;
size_t pos_;
};
size_t without_update_ = 0;
- std::vector<Event> events_;
- std::vector<Limit> limits_;
+ vector<Event> events_;
+ vector<Limit> limits_;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.cpp
index d4e60d6e29..64b07a9c04 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,10 +9,11 @@
char disable_linker_warning_about_empty_file_gzip_cpp TD_UNUSED;
#if TD_HAVE_ZLIB
-#include "td/utils/logging.h"
+#include "td/utils/SliceBuilder.h"
#include <cstring>
#include <limits>
+#include <utility>
#include <zlib.h>
@@ -32,23 +33,23 @@ class Gzip::Impl {
};
Status Gzip::init_encode() {
- CHECK(mode_ == Empty);
+ CHECK(mode_ == Mode::Empty);
init_common();
- mode_ = Encode;
+ mode_ = Mode::Encode;
int ret = deflateInit2(&impl_->stream_, 6, Z_DEFLATED, 15, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
if (ret != Z_OK) {
- return Status::Error("zlib deflate init failed");
+ return Status::Error(PSLICE() << "zlib deflate init failed: " << ret);
}
return Status::OK();
}
Status Gzip::init_decode() {
- CHECK(mode_ == Empty);
+ CHECK(mode_ == Mode::Empty);
init_common();
- mode_ = Decode;
+ mode_ = Mode::Decode;
int ret = inflateInit2(&impl_->stream_, MAX_WBITS + 32);
if (ret != Z_OK) {
- return Status::Error("zlib inflate init failed");
+ return Status::Error(PSLICE() << "zlib inflate init failed: " << ret);
}
return Status::OK();
}
@@ -75,19 +76,19 @@ void Gzip::set_output(MutableSlice output) {
Result<Gzip::State> Gzip::run() {
while (true) {
int ret;
- if (mode_ == Decode) {
+ if (mode_ == Mode::Decode) {
ret = inflate(&impl_->stream_, Z_NO_FLUSH);
} else {
ret = deflate(&impl_->stream_, close_input_flag_ ? Z_FINISH : Z_NO_FLUSH);
}
if (ret == Z_OK) {
- return Running;
+ return State::Running;
}
if (ret == Z_STREAM_END) {
// TODO(now): fail if input is not empty;
clear();
- return Done;
+ return State::Done;
}
clear();
return Status::Error(PSLICE() << "zlib error " << ret);
@@ -118,20 +119,36 @@ void Gzip::init_common() {
}
void Gzip::clear() {
- if (mode_ == Decode) {
+ if (mode_ == Mode::Decode) {
inflateEnd(&impl_->stream_);
- } else if (mode_ == Encode) {
+ } else if (mode_ == Mode::Encode) {
deflateEnd(&impl_->stream_);
}
- mode_ = Empty;
+ mode_ = Mode::Empty;
}
Gzip::Gzip() : impl_(make_unique<Impl>()) {
}
-Gzip::Gzip(Gzip &&other) = default;
+Gzip::Gzip(Gzip &&other) noexcept : Gzip() {
+ swap(other);
+}
-Gzip &Gzip::operator=(Gzip &&other) = default;
+Gzip &Gzip::operator=(Gzip &&other) noexcept {
+ CHECK(this != &other);
+ clear();
+ swap(other);
+ return *this;
+}
+
+void Gzip::swap(Gzip &other) {
+ using std::swap;
+ swap(impl_, other.impl_);
+ swap(input_size_, other.input_size_);
+ swap(output_size_, other.output_size_);
+ swap(close_input_flag_, other.close_input_flag_);
+ swap(mode_, other.mode_);
+}
Gzip::~Gzip() {
clear();
@@ -140,7 +157,7 @@ Gzip::~Gzip() {
BufferSlice gzdecode(Slice s) {
Gzip gzip;
gzip.init_decode().ensure();
- auto message = ChainBufferWriter::create_empty();
+ ChainBufferWriter message;
gzip.set_input(s);
gzip.close_input();
double k = 2;
@@ -151,7 +168,7 @@ BufferSlice gzdecode(Slice s) {
return BufferSlice();
}
auto state = r_state.ok();
- if (state == Gzip::Done) {
+ if (state == Gzip::State::Done) {
message.confirm_append(gzip.flush_output());
break;
}
@@ -167,12 +184,12 @@ BufferSlice gzdecode(Slice s) {
return message.extract_reader().move_as_buffer_slice();
}
-BufferSlice gzencode(Slice s, double k) {
+BufferSlice gzencode(Slice s, double max_compression_ratio) {
Gzip gzip;
gzip.init_encode().ensure();
gzip.set_input(s);
gzip.close_input();
- size_t max_size = static_cast<size_t>(static_cast<double>(s.size()) * k);
+ auto max_size = static_cast<size_t>(static_cast<double>(s.size()) * max_compression_ratio);
BufferWriter message{max_size};
gzip.set_output(message.prepare_append());
auto r_state = gzip.run();
@@ -180,7 +197,7 @@ BufferSlice gzencode(Slice s, double k) {
return BufferSlice();
}
auto state = r_state.ok();
- if (state != Gzip::Done) {
+ if (state != Gzip::State::Done) {
return BufferSlice();
}
message.confirm_append(gzip.flush_output());
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.h
index dd5fba5bf5..d7b68c5e45 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Gzip.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -20,15 +20,15 @@ class Gzip {
Gzip();
Gzip(const Gzip &) = delete;
Gzip &operator=(const Gzip &) = delete;
- Gzip(Gzip &&other);
- Gzip &operator=(Gzip &&other);
+ Gzip(Gzip &&other) noexcept;
+ Gzip &operator=(Gzip &&other) noexcept;
~Gzip();
- enum Mode { Empty, Encode, Decode };
+ enum class Mode { Empty, Encode, Decode };
Status init(Mode mode) TD_WARN_UNUSED_RESULT {
- if (mode == Encode) {
+ if (mode == Mode::Encode) {
return init_encode();
- } else if (mode == Decode) {
+ } else if (mode == Mode::Decode) {
return init_decode();
}
clear();
@@ -79,7 +79,7 @@ class Gzip {
return res;
}
- enum State { Running, Done };
+ enum class State { Running, Done };
Result<State> run() TD_WARN_UNUSED_RESULT;
private:
@@ -89,15 +89,17 @@ class Gzip {
size_t input_size_ = 0;
size_t output_size_ = 0;
bool close_input_flag_ = false;
- Mode mode_ = Empty;
+ Mode mode_ = Mode::Empty;
void init_common();
void clear();
+
+ void swap(Gzip &other);
};
BufferSlice gzdecode(Slice s);
-BufferSlice gzencode(Slice s, double k = 0.9);
+BufferSlice gzencode(Slice s, double max_compression_ratio);
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.cpp
index d225ef800e..d321b68ccf 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,62 +9,57 @@
char disable_linker_warning_about_empty_file_gzipbyteflow_cpp TD_UNUSED;
#if TD_HAVE_ZLIB
-#include "td/utils/logging.h"
+#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
-void GzipByteFlow::loop() {
- while (true) {
- if (gzip_.need_input()) {
- auto slice = input_->prepare_read();
- if (slice.empty()) {
- if (!is_input_active_) {
- gzip_.close_input();
- } else {
- break;
- }
+bool GzipByteFlow::loop() {
+ if (gzip_.need_input()) {
+ auto slice = input_->prepare_read();
+ if (slice.empty()) {
+ if (!is_input_active_) {
+ gzip_.close_input();
} else {
- gzip_.set_input(input_->prepare_read());
+ return false;
}
+ } else {
+ gzip_.set_input(input_->prepare_read());
}
- if (gzip_.need_output()) {
- auto slice = output_.prepare_append();
- CHECK(!slice.empty());
- gzip_.set_output(slice);
- }
- auto r_state = gzip_.run();
- auto output_size = gzip_.flush_output();
- if (output_size) {
- uncommited_size_ += output_size;
- total_output_size_ += output_size;
- if (total_output_size_ > max_output_size_) {
- return finish(Status::Error("Max output size limit exceeded"));
- }
- output_.confirm_append(output_size);
+ }
+ if (gzip_.need_output()) {
+ auto slice = output_.prepare_append();
+ CHECK(!slice.empty());
+ gzip_.set_output(slice);
+ }
+ auto r_state = gzip_.run();
+ auto output_size = gzip_.flush_output();
+ if (output_size) {
+ uncommitted_size_ += output_size;
+ total_output_size_ += output_size;
+ if (total_output_size_ > max_output_size_) {
+ finish(Status::Error("Max output size limit exceeded"));
+ return false;
}
+ output_.confirm_append(output_size);
+ }
- auto input_size = gzip_.flush_input();
- if (input_size) {
- input_->confirm_read(input_size);
- }
- if (r_state.is_error()) {
- return finish(r_state.move_as_error());
- }
- auto state = r_state.ok();
- if (state == Gzip::Done) {
- on_output_updated();
- return consume_input();
- }
+ auto input_size = gzip_.flush_input();
+ if (input_size) {
+ input_->confirm_read(input_size);
}
- if (uncommited_size_ >= MIN_UPDATE_SIZE) {
- uncommited_size_ = 0;
- on_output_updated();
+ if (r_state.is_error()) {
+ finish(r_state.move_as_error());
+ return false;
}
+ auto state = r_state.ok();
+ if (state == Gzip::State::Done) {
+ consume_input();
+ return false;
+ }
+ return true;
}
-constexpr size_t GzipByteFlow::MIN_UPDATE_SIZE;
-
} // namespace td
#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.h
index c7e07abd0a..94a3a3ea50 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/GzipByteFlow.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -34,14 +34,13 @@ class GzipByteFlow final : public ByteFlowBase {
max_output_size_ = max_output_size;
}
- void loop() override;
+ bool loop() final;
private:
Gzip gzip_;
- size_t uncommited_size_ = 0;
+ size_t uncommitted_size_ = 0;
size_t total_output_size_ = 0;
size_t max_output_size_ = std::numeric_limits<size_t>::max();
- static constexpr size_t MIN_UPDATE_SIZE = 1 << 14;
};
#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Hash.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Hash.h
new file mode 100644
index 0000000000..8c500daf09
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Hash.h
@@ -0,0 +1,70 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#if TD_HAVE_ABSL
+#include <absl/hash/hash.h>
+#endif
+
+#include <utility>
+
+namespace td {
+// A simple wrapper for absl::flat_hash_map, std::unordered_map and probably some our implementaion of hash map in
+// the future
+
+// We will introduce out own Hashing utility like an absl one.
+class Hasher {
+ public:
+ Hasher() = default;
+ explicit Hasher(size_t init_value) : hash_(init_value) {
+ }
+ std::size_t finalize() const {
+ return hash_;
+ }
+
+ static Hasher combine(Hasher hasher, size_t value) {
+ hasher.hash_ ^= value;
+ return hasher;
+ }
+
+ template <class A, class B>
+ static Hasher combine(Hasher hasher, const std::pair<A, B> &value) {
+ hasher = AbslHashValue(std::move(hasher), value.first);
+ hasher = AbslHashValue(std::move(hasher), value.second);
+ return hasher;
+ }
+
+ private:
+ std::size_t hash_{0};
+};
+
+template <class IgnoreT>
+class TdHash {
+ public:
+ template <class T>
+ std::size_t operator()(const T &value) const noexcept {
+ return AbslHashValue(Hasher(), value).finalize();
+ }
+};
+
+#if TD_HAVE_ABSL
+template <class T>
+using AbslHash = absl::Hash<T>;
+#else
+template <class T>
+using AbslHash = TdHash<T>;
+#endif
+
+// default hash implementations
+template <class H, class T>
+decltype(H::combine(std::declval<H>(), std::declval<T>())) AbslHashValue(H hasher, const T &value) {
+ return H::combine(std::move(hasher), value);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/HashMap.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/HashMap.h
new file mode 100644
index 0000000000..7e0ba4bf07
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/HashMap.h
@@ -0,0 +1,27 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/Hash.h"
+
+#if TD_HAVE_ABSL
+#include <absl/container/flat_hash_map.h>
+#else
+#include <unordered_map>
+#endif
+
+namespace td {
+
+#if TD_HAVE_ABSL
+template <class Key, class Value, class H = AbslHash<Key>>
+using HashMap = absl::flat_hash_map<Key, Value, H>;
+#else
+template <class Key, class Value, class H = AbslHash<Key>>
+using HashMap = std::unordered_map<Key, Value, H>;
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/HashSet.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/HashSet.h
new file mode 100644
index 0000000000..e49e8b94e3
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/HashSet.h
@@ -0,0 +1,27 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/Hash.h"
+
+#if TD_HAVE_ABSL
+#include <absl/container/flat_hash_set.h>
+#else
+#include <unordered_set>
+#endif
+
+namespace td {
+
+#if TD_HAVE_ABSL
+template <class Key, class H = AbslHash<Key>>
+using HashSet = absl::flat_hash_set<Key, H>;
+#else
+template <class Key, class H = AbslHash<Key>>
+using HashSet = std::unordered_set<Key, H>;
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/HashTableUtils.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/HashTableUtils.h
new file mode 100644
index 0000000000..9d72f63c59
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/HashTableUtils.h
@@ -0,0 +1,72 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#include <cstdint>
+#include <functional>
+
+namespace td {
+
+template <class KeyT>
+bool is_hash_table_key_empty(const KeyT &key) {
+ return key == KeyT();
+}
+
+inline uint32 randomize_hash(uint32 h) {
+ h ^= h >> 16;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+ return h;
+}
+
+template <class Type>
+struct Hash {
+ uint32 operator()(const Type &value) const;
+};
+
+template <class Type>
+struct Hash<Type *> {
+ uint32 operator()(Type *pointer) const {
+ return Hash<uint64>()(reinterpret_cast<std::uintptr_t>(pointer));
+ }
+};
+
+template <>
+inline uint32 Hash<char>::operator()(const char &value) const {
+ return randomize_hash(static_cast<uint32>(value));
+}
+
+template <>
+inline uint32 Hash<int32>::operator()(const int32 &value) const {
+ return randomize_hash(static_cast<uint32>(value));
+}
+
+template <>
+inline uint32 Hash<uint32>::operator()(const uint32 &value) const {
+ return randomize_hash(value);
+}
+
+template <>
+inline uint32 Hash<int64>::operator()(const int64 &value) const {
+ return randomize_hash(static_cast<uint32>(value + (value >> 32)));
+}
+
+template <>
+inline uint32 Hash<uint64>::operator()(const uint64 &value) const {
+ return randomize_hash(static_cast<uint32>(value + (value >> 32)));
+}
+
+template <>
+inline uint32 Hash<string>::operator()(const string &value) const {
+ return static_cast<uint32>(std::hash<string>()(value));
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/HazardPointers.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/HazardPointers.h
index e13dc8022e..3ed41a0c9a 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/HazardPointers.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/HazardPointers.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,20 +7,25 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include <array>
#include <atomic>
+#include <memory>
namespace td {
-template <class T, int MaxPointersN = 1>
+template <class T, int MaxPointersN = 1, class Deleter = std::default_delete<T>>
class HazardPointers {
public:
explicit HazardPointers(size_t threads_n) : threads_(threads_n) {
for (auto &data : threads_) {
- for (auto &ptr : data.hazard) {
+ for (auto &ptr : data.hazard_) {
+// workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658
+#if TD_GCC && GCC_VERSION <= 40902
ptr = nullptr;
+#else
+ std::atomic_init(&ptr, static_cast<T *>(nullptr));
+#endif
}
}
}
@@ -31,12 +36,17 @@ class HazardPointers {
class Holder {
public:
- T *protect(std::atomic<T *> &to_protect) {
+ template <class S>
+ S *protect(std::atomic<S *> &to_protect) {
return do_protect(hazard_ptr_, to_protect);
}
+ Holder(HazardPointers &hp, size_t thread_id, size_t pos) : Holder(hp.get_hazard_ptr(thread_id, pos)) {
+ CHECK(hazard_ptr_.load() == 0);
+ hazard_ptr_.store(reinterpret_cast<T *>(1));
+ }
Holder(const Holder &other) = delete;
Holder &operator=(const Holder &other) = delete;
- Holder(Holder &&other) = default; // TODO
+ Holder(Holder &&other) = delete;
Holder &operator=(Holder &&other) = delete;
~Holder() {
clear();
@@ -52,20 +62,16 @@ class HazardPointers {
std::atomic<T *> &hazard_ptr_;
};
- Holder get_holder(size_t thread_id, size_t pos) {
- return Holder(get_hazard_ptr(thread_id, pos));
- }
-
void retire(size_t thread_id, T *ptr = nullptr) {
CHECK(thread_id < threads_.size());
auto &data = threads_[thread_id];
if (ptr) {
- data.to_delete.push_back(std::unique_ptr<T>(ptr));
+ data.to_delete_.push_back(std::unique_ptr<T, Deleter>(ptr));
}
- for (auto it = data.to_delete.begin(); it != data.to_delete.end();) {
+ for (auto it = data.to_delete_.begin(); it != data.to_delete_.end();) {
if (!is_protected(it->get())) {
it->reset();
- it = data.to_delete.erase(it);
+ it = data.to_delete_.erase(it);
} else {
++it;
}
@@ -82,32 +88,33 @@ class HazardPointers {
size_t to_delete_size_unsafe() const {
size_t res = 0;
- for (auto &thread : threads_) {
- res += thread.to_delete.size();
+ for (auto &thread_data : threads_) {
+ res += thread_data.to_delete_.size();
}
return res;
}
private:
struct ThreadData {
- std::array<std::atomic<T *>, MaxPointersN> hazard;
+ std::array<std::atomic<T *>, MaxPointersN> hazard_;
char pad[TD_CONCURRENCY_PAD - sizeof(std::array<std::atomic<T *>, MaxPointersN>)];
// stupid gc
- std::vector<std::unique_ptr<T>> to_delete;
- char pad2[TD_CONCURRENCY_PAD - sizeof(std::vector<std::unique_ptr<T>>)];
+ std::vector<std::unique_ptr<T, Deleter>> to_delete_;
+ char pad2[TD_CONCURRENCY_PAD - sizeof(std::vector<std::unique_ptr<T, Deleter>>)];
};
std::vector<ThreadData> threads_;
char pad2[TD_CONCURRENCY_PAD - sizeof(std::vector<ThreadData>)];
- static T *do_protect(std::atomic<T *> &hazard_ptr, std::atomic<T *> &to_protect) {
+ template <class S>
+ static S *do_protect(std::atomic<T *> &hazard_ptr, std::atomic<S *> &to_protect) {
T *saved = nullptr;
T *to_save;
while ((to_save = to_protect.load()) != saved) {
hazard_ptr.store(to_save);
saved = to_save;
}
- return saved;
+ return static_cast<S *>(saved);
}
static void do_clear(std::atomic<T *> &hazard_ptr) {
@@ -115,8 +122,8 @@ class HazardPointers {
}
bool is_protected(T *ptr) {
- for (auto &thread : threads_) {
- for (auto &hazard_ptr : thread.hazard) {
+ for (auto &thread_data : threads_) {
+ for (auto &hazard_ptr : thread_data.hazard_) {
if (hazard_ptr.load() == ptr) {
return true;
}
@@ -126,7 +133,8 @@ class HazardPointers {
}
std::atomic<T *> &get_hazard_ptr(size_t thread_id, size_t pos) {
- return threads_[thread_id].hazard[pos];
+ CHECK(thread_id < threads_.size());
+ return threads_[thread_id].hazard_[pos];
}
};
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Heap.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Heap.h
index 54ee391497..154b87089a 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Heap.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Heap.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,6 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
namespace td {
@@ -21,7 +20,7 @@ struct HeapNode {
void remove() {
pos_ = -1;
}
- int pos_ = -1;
+ int32 pos_ = -1;
};
template <class KeyT, int K = 4>
@@ -38,23 +37,33 @@ class KHeap {
return array_[0].key_;
}
+ KeyT get_key(const HeapNode *node) const {
+ auto pos = static_cast<size_t>(node->pos_);
+ CHECK(pos < array_.size());
+ return array_[pos].key_;
+ }
+
+ const HeapNode *top() const {
+ return array_[0].node_;
+ }
+
HeapNode *pop() {
CHECK(!empty());
HeapNode *result = array_[0].node_;
result->remove();
- erase(0);
+ erase(static_cast<size_t>(0));
return result;
}
void insert(KeyT key, HeapNode *node) {
CHECK(!node->in_heap());
array_.push_back({key, node});
- fix_up(static_cast<int>(array_.size()) - 1);
+ fix_up(array_.size() - 1);
}
void fix(KeyT key, HeapNode *node) {
- CHECK(node->in_heap());
- int pos = node->pos_;
+ auto pos = static_cast<size_t>(node->pos_);
+ CHECK(pos < array_.size());
KeyT old_key = array_[pos].key_;
array_[pos].key_ = key;
if (key < old_key) {
@@ -65,14 +74,21 @@ class KHeap {
}
void erase(HeapNode *node) {
- CHECK(node->in_heap());
- int pos = node->pos_;
+ auto pos = static_cast<size_t>(node->pos_);
node->remove();
+ CHECK(pos < array_.size());
erase(pos);
}
template <class F>
- void for_each(F &f) const {
+ void for_each(F &&f) const {
+ for (auto &it : array_) {
+ f(it.key_, it.node_);
+ }
+ }
+
+ template <class F>
+ void for_each(F &&f) {
for (auto &it : array_) {
f(it.key_, it.node_);
}
@@ -81,7 +97,7 @@ class KHeap {
void check() const {
for (size_t i = 0; i < array_.size(); i++) {
for (size_t j = i * K + 1; j < i * K + 1 + K && j < array_.size(); j++) {
- CHECK(array_[i].key_ <= array_[j].key_) << i << " " << j;
+ CHECK(array_[i].key_ <= array_[j].key_);
}
}
}
@@ -93,34 +109,34 @@ class KHeap {
};
vector<Item> array_;
- void fix_up(int pos) {
+ void fix_up(size_t pos) {
auto item = array_[pos];
while (pos) {
- int parent_pos = (pos - 1) / K;
+ auto parent_pos = (pos - 1) / K;
auto parent_item = array_[parent_pos];
if (parent_item.key_ < item.key_) {
break;
}
- parent_item.node_->pos_ = pos;
+ parent_item.node_->pos_ = static_cast<int32>(pos);
array_[pos] = parent_item;
pos = parent_pos;
}
- item.node_->pos_ = pos;
+ item.node_->pos_ = static_cast<int32>(pos);
array_[pos] = item;
}
- void fix_down(int pos) {
+ void fix_down(size_t pos) {
auto item = array_[pos];
while (true) {
- int left_pos = pos * K + 1;
- int right_pos = min(left_pos + K, static_cast<int>(array_.size()));
- int next_pos = pos;
+ auto left_pos = pos * K + 1;
+ auto right_pos = min(left_pos + K, array_.size());
+ auto next_pos = pos;
KeyT next_key = item.key_;
- for (int i = left_pos; i < right_pos; i++) {
+ for (auto i = left_pos; i < right_pos; i++) {
KeyT i_key = array_[i].key_;
if (i_key < next_key) {
next_key = i_key;
@@ -131,21 +147,24 @@ class KHeap {
break;
}
array_[pos] = array_[next_pos];
- array_[pos].node_->pos_ = pos;
+ array_[pos].node_->pos_ = static_cast<int32>(pos);
pos = next_pos;
}
- item.node_->pos_ = pos;
+ item.node_->pos_ = static_cast<int32>(pos);
array_[pos] = item;
}
- void erase(int pos) {
+ void erase(size_t pos) {
array_[pos] = array_.back();
array_.pop_back();
- if (pos < static_cast<int>(array_.size())) {
+ if (pos < array_.size()) {
fix_down(pos);
fix_up(pos);
}
+ if (array_.capacity() > 50 && array_.size() < array_.capacity() / 4) {
+ array_.shrink_to_fit();
+ }
}
};
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.cpp
index 1e7449a668..1041b95d8b 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.cpp
@@ -1,49 +1,23 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/Hints.h"
+#include "td/utils/algorithm.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Slice.h"
-#include "td/utils/unicode.h"
+#include "td/utils/translit.h"
#include "td/utils/utf8.h"
#include <algorithm>
namespace td {
-vector<string> Hints::get_words(Slice name) {
- bool in_word = false;
- string word;
- vector<string> words;
- auto pos = name.ubegin();
- auto end = name.uend();
- while (pos != end) {
- uint32 code;
- pos = next_utf8_unsafe(pos, &code);
-
- code = prepare_search_character(code);
- if (code == 0) {
- continue;
- }
- if (code == ' ') {
- if (in_word) {
- words.push_back(std::move(word));
- word.clear();
- in_word = false;
- }
- } else {
- in_word = true;
- append_utf8_character(word, code);
- }
- }
- if (in_word) {
- words.push_back(std::move(word));
- }
+vector<string> Hints::fix_words(vector<string> words) {
std::sort(words.begin(), words.end());
size_t new_words_size = 0;
@@ -52,14 +26,39 @@ vector<string> Hints::get_words(Slice name) {
if (i != new_words_size) {
words[new_words_size] = std::move(words[i]);
}
- // LOG(ERROR) << "Get word " << words[new_words_size];
new_words_size++;
}
}
+ if (new_words_size == 1 && words[0].empty()) {
+ new_words_size = 0;
+ }
words.resize(new_words_size);
return words;
}
+vector<string> Hints::get_words(Slice name) {
+ return fix_words(utf8_get_search_words(name));
+}
+
+void Hints::add_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys) {
+ vector<KeyT> &keys = word_to_keys[word];
+ CHECK(!td::contains(keys, key));
+ keys.push_back(key);
+}
+
+void Hints::delete_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys) {
+ vector<KeyT> &keys = word_to_keys[word];
+ auto key_it = std::find(keys.begin(), keys.end(), key);
+ CHECK(key_it != keys.end());
+ if (keys.size() == 1) {
+ word_to_keys.erase(word);
+ } else {
+ CHECK(keys.size() > 1);
+ *key_it = keys.back();
+ keys.pop_back();
+ }
+}
+
void Hints::add(KeyT key, Slice name) {
// LOG(ERROR) << "Add " << key << ": " << name;
auto it = key_to_name_.find(key);
@@ -67,19 +66,19 @@ void Hints::add(KeyT key, Slice name) {
if (it->second == name) {
return;
}
- auto old_words = get_words(it->second);
- for (auto &old_word : old_words) {
- vector<KeyT> &keys = word_to_keys_[old_word];
- auto key_it = std::find(keys.begin(), keys.end(), key);
- CHECK(key_it != keys.end());
- if (keys.size() == 1) {
- word_to_keys_.erase(old_word);
- } else {
- CHECK(keys.size() > 1);
- *key_it = keys.back();
- keys.pop_back();
+ vector<string> old_transliterations;
+ for (auto &old_word : get_words(it->second)) {
+ delete_word(old_word, key, word_to_keys_);
+
+ for (auto &w : get_word_transliterations(old_word, false)) {
+ if (w != old_word) {
+ old_transliterations.push_back(std::move(w));
+ }
}
}
+ for (auto &word : fix_words(old_transliterations)) {
+ delete_word(word, key, translit_word_to_keys_);
+ }
}
if (name.empty()) {
if (it != key_to_name_.end()) {
@@ -88,12 +87,21 @@ void Hints::add(KeyT key, Slice name) {
key_to_rating_.erase(key);
return;
}
- auto words = get_words(name);
- for (auto &word : words) {
- vector<KeyT> &keys = word_to_keys_[word];
- CHECK(std::find(keys.begin(), keys.end(), key) == keys.end());
- keys.push_back(key);
+
+ vector<string> transliterations;
+ for (auto &word : get_words(name)) {
+ add_word(word, key, word_to_keys_);
+
+ for (auto &w : get_word_transliterations(word, false)) {
+ if (w != word) {
+ transliterations.push_back(std::move(w));
+ }
+ }
+ }
+ for (auto &word : fix_words(transliterations)) {
+ add_word(word, key, translit_word_to_keys_);
}
+
key_to_name_[key] = name.str();
}
@@ -102,17 +110,24 @@ void Hints::set_rating(KeyT key, RatingT rating) {
key_to_rating_[key] = rating;
}
-vector<Hints::KeyT> Hints::search_word(const string &word) const {
- // LOG(ERROR) << "Search word " << word;
- vector<KeyT> results;
- auto it = word_to_keys_.lower_bound(word);
- while (it != word_to_keys_.end() && begins_with(it->first, word)) {
+void Hints::add_search_results(vector<KeyT> &results, const string &word,
+ const std::map<string, vector<KeyT>> &word_to_keys) {
+ LOG(DEBUG) << "Search for word " << word;
+ auto it = word_to_keys.lower_bound(word);
+ while (it != word_to_keys.end() && begins_with(it->first, word)) {
results.insert(results.end(), it->second.begin(), it->second.end());
++it;
}
+}
+
+vector<Hints::KeyT> Hints::search_word(const string &word) const {
+ vector<KeyT> results;
+ add_search_results(results, word, translit_word_to_keys_);
+ for (const auto &w : get_word_transliterations(word, true)) {
+ add_search_results(results, w, word_to_keys_);
+ }
- std::sort(results.begin(), results.end());
- results.erase(std::unique(results.begin(), results.end()), results.end());
+ td::unique(results);
return results;
}
@@ -169,7 +184,7 @@ std::pair<size_t, vector<Hints::KeyT>> Hints::search(Slice query, int32 limit, b
}
bool Hints::has_key(KeyT key) const {
- return key_to_name_.find(key) != key_to_name_.end();
+ return key_to_name_.count(key) > 0;
}
string Hints::key_to_string(KeyT key) const {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.h
index 645896684a..f069da20d0 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Hints.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,7 @@
#pragma once
#include "td/utils/common.h"
+#include "td/utils/HashTableUtils.h"
#include "td/utils/Slice.h"
#include <map>
@@ -41,17 +42,26 @@ class Hints {
size_t size() const;
+ static vector<string> fix_words(vector<string> words);
+
private:
std::map<string, vector<KeyT>> word_to_keys_;
- std::unordered_map<KeyT, string> key_to_name_;
- std::unordered_map<KeyT, RatingT> key_to_rating_;
+ std::map<string, vector<KeyT>> translit_word_to_keys_;
+ std::unordered_map<KeyT, string, Hash<KeyT>> key_to_name_;
+ std::unordered_map<KeyT, RatingT, Hash<KeyT>> key_to_rating_;
+
+ static void add_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys);
+ static void delete_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys);
static vector<string> get_words(Slice name);
+ static void add_search_results(vector<KeyT> &results, const string &word,
+ const std::map<string, vector<KeyT>> &word_to_keys);
+
vector<KeyT> search_word(const string &word) const;
class CompareByRating {
- const std::unordered_map<KeyT, RatingT> &key_to_rating_;
+ const std::unordered_map<KeyT, RatingT, Hash<KeyT>> &key_to_rating_;
RatingT get_rating(const KeyT &key) const {
auto it = key_to_rating_.find(key);
@@ -62,7 +72,8 @@ class Hints {
}
public:
- explicit CompareByRating(const std::unordered_map<KeyT, RatingT> &key_to_rating) : key_to_rating_(key_to_rating) {
+ explicit CompareByRating(const std::unordered_map<KeyT, RatingT, Hash<KeyT>> &key_to_rating)
+ : key_to_rating_(key_to_rating) {
}
bool operator()(const KeyT &lhs, const KeyT &rhs) const {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.cpp
index 55b66f7b3a..e793f940a8 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,16 +10,19 @@
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Parser.h"
+#include "td/utils/port/IPAddress.h"
+
+#include <algorithm>
namespace td {
string HttpUrl::get_url() const {
string result;
switch (protocol_) {
- case Protocol::HTTP:
+ case Protocol::Http:
result += "http://";
break;
- case Protocol::HTTPS:
+ case Protocol::Https:
result += "https://";
break;
default:
@@ -29,39 +32,32 @@ string HttpUrl::get_url() const {
result += userinfo_;
result += '@';
}
- if (is_ipv6) {
- result += '[';
- }
result += host_;
- if (is_ipv6) {
- result += ']';
- }
if (specified_port_ > 0) {
result += ':';
result += to_string(specified_port_);
}
- CHECK(!query_.empty() && query_[0] == '/');
+ LOG_CHECK(!query_.empty() && query_[0] == '/') << query_;
result += query_;
return result;
}
-Result<HttpUrl> parse_url(MutableSlice url, HttpUrl::Protocol default_protocol) {
+Result<HttpUrl> parse_url(Slice url, HttpUrl::Protocol default_protocol) {
// url == [https?://][userinfo@]host[:port]
- Parser parser(url);
- string protocol_str = to_lower(parser.read_till_nofail(':'));
+ ConstParser parser(url);
+ string protocol_str = to_lower(parser.read_till_nofail(":/?#@[]"));
HttpUrl::Protocol protocol;
- if (parser.start_with("://")) {
- parser.advance(3);
+ if (parser.try_skip("://")) {
if (protocol_str == "http") {
- protocol = HttpUrl::Protocol::HTTP;
+ protocol = HttpUrl::Protocol::Http;
} else if (protocol_str == "https") {
- protocol = HttpUrl::Protocol::HTTPS;
+ protocol = HttpUrl::Protocol::Https;
} else {
return Status::Error("Unsupported URL protocol");
}
} else {
- parser = Parser(url);
+ parser = ConstParser(url);
protocol = default_protocol;
}
Slice userinfo_host_port = parser.read_till_nofail("/?#");
@@ -73,7 +69,16 @@ Result<HttpUrl> parse_url(MutableSlice url, HttpUrl::Protocol default_protocol)
}
Slice userinfo_host;
if (colon > userinfo_host_port.begin() && *colon == ':') {
- port = to_integer<int>(Slice(colon + 1, userinfo_host_port.end()));
+ Slice port_slice(colon + 1, userinfo_host_port.end());
+ while (port_slice.size() > 1 && port_slice[0] == '0') {
+ port_slice.remove_prefix(1);
+ }
+ auto r_port = to_integer_safe<int>(port_slice);
+ if (r_port.is_error() || r_port.ok() == 0) {
+ port = -1;
+ } else {
+ port = r_port.ok();
+ }
userinfo_host = Slice(userinfo_host_port.begin(), colon);
} else {
userinfo_host = userinfo_host_port;
@@ -88,20 +93,26 @@ Result<HttpUrl> parse_url(MutableSlice url, HttpUrl::Protocol default_protocol)
bool is_ipv6 = false;
if (!host.empty() && host[0] == '[' && host.back() == ']') {
- host.remove_prefix(1);
- host.remove_suffix(1);
+ IPAddress ip_address;
+ if (ip_address.init_ipv6_port(host.str(), 1).is_error()) {
+ return Status::Error("Wrong IPv6 address specified in the URL");
+ }
+ CHECK(ip_address.is_ipv6());
is_ipv6 = true;
}
if (host.empty()) {
return Status::Error("URL host is empty");
}
+ if (host == ".") {
+ return Status::Error("Host is invalid");
+ }
int specified_port = port;
if (port == 0) {
- if (protocol == HttpUrl::Protocol::HTTP) {
+ if (protocol == HttpUrl::Protocol::Http) {
port = 80;
} else {
- CHECK(protocol == HttpUrl::Protocol::HTTPS);
+ CHECK(protocol == HttpUrl::Protocol::Https);
port = 443;
}
}
@@ -111,7 +122,7 @@ Result<HttpUrl> parse_url(MutableSlice url, HttpUrl::Protocol default_protocol)
query.remove_suffix(1);
}
if (query.empty()) {
- query = "/";
+ query = Slice("/");
}
string query_str;
if (query[0] != '/') {
@@ -130,6 +141,14 @@ Result<HttpUrl> parse_url(MutableSlice url, HttpUrl::Protocol default_protocol)
string host_str = to_lower(host);
for (size_t i = 0; i < host_str.size(); i++) {
char c = host_str[i];
+ if (is_ipv6) {
+ if (i == 0 || i + 1 == host_str.size() || c == ':' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
+ c == '.') {
+ continue;
+ }
+ return Status::Error("Wrong IPv6 URL host");
+ }
+
if (('a' <= c && c <= 'z') || c == '.' || ('0' <= c && c <= '9') || c == '-' || c == '_' || c == '!' || c == '$' ||
c == ',' || c == '~' || c == '*' || c == '\'' || c == '(' || c == ')' || c == ';' || c == '&' || c == '+' ||
c == '=') {
@@ -145,9 +164,11 @@ Result<HttpUrl> parse_url(MutableSlice url, HttpUrl::Protocol default_protocol)
continue;
}
}
+ return Status::Error("Wrong percent-encoded symbol in URL host");
}
+
// all other symbols aren't allowed
- unsigned char uc = static_cast<unsigned char>(c);
+ auto uc = static_cast<unsigned char>(c);
if (uc >= 128) {
// but we allow plain UTF-8 symbols
continue;
@@ -159,11 +180,66 @@ Result<HttpUrl> parse_url(MutableSlice url, HttpUrl::Protocol default_protocol)
}
StringBuilder &operator<<(StringBuilder &sb, const HttpUrl &url) {
- sb << tag("protocol", url.protocol_ == HttpUrl::Protocol::HTTP ? "HTTP" : "HTTPS") << tag("userinfo", url.userinfo_)
+ sb << tag("protocol", url.protocol_ == HttpUrl::Protocol::Http ? "HTTP" : "HTTPS") << tag("userinfo", url.userinfo_)
<< tag("host", url.host_) << tag("port", url.port_) << tag("query", url.query_);
return sb;
}
+HttpUrlQuery parse_url_query(Slice query) {
+ if (!query.empty() && query[0] == '/') {
+ query.remove_prefix(1);
+ }
+
+ size_t path_size = 0;
+ while (path_size < query.size() && query[path_size] != '?' && query[path_size] != '#') {
+ path_size++;
+ }
+
+ HttpUrlQuery result;
+ result.path_ = full_split(url_decode(query.substr(0, path_size), false), '/');
+ while (!result.path_.empty() && result.path_.back().empty()) {
+ result.path_.pop_back();
+ }
+
+ if (path_size < query.size() && query[path_size] == '?') {
+ query = query.substr(path_size + 1);
+ query.truncate(query.find('#'));
+
+ ConstParser parser(query);
+ while (!parser.data().empty()) {
+ auto key_value = split(parser.read_till_nofail('&'), '=');
+ parser.skip_nofail('&');
+ auto key = url_decode(key_value.first, true);
+ if (!key.empty()) {
+ result.args_.emplace_back(std::move(key), url_decode(key_value.second, true));
+ }
+ }
+ CHECK(parser.status().is_ok());
+ }
+
+ return result;
+}
+
+bool HttpUrlQuery::has_arg(Slice key) const {
+ auto it =
+ std::find_if(args_.begin(), args_.end(), [&key](const std::pair<string, string> &s) { return s.first == key; });
+ return it != args_.end();
+}
+
+Slice HttpUrlQuery::get_arg(Slice key) const {
+ auto it =
+ std::find_if(args_.begin(), args_.end(), [&key](const std::pair<string, string> &s) { return s.first == key; });
+ return it == args_.end() ? Slice() : it->second;
+}
+
+string get_url_host(Slice url) {
+ auto r_http_url = parse_url(url);
+ if (r_http_url.is_error()) {
+ return string();
+ }
+ return r_http_url.ok().host_;
+}
+
string get_url_query_file_name(const string &query) {
Slice query_slice = query;
query_slice.truncate(query.find_first_of("?#"));
@@ -175,10 +251,8 @@ string get_url_query_file_name(const string &query) {
return query_slice.str();
}
-string get_url_file_name(const string &url) {
- // TODO remove copy
- string url_copy = url;
- auto r_http_url = parse_url(url_copy);
+string get_url_file_name(Slice url) {
+ auto r_http_url = parse_url(url);
if (r_http_url.is_error()) {
LOG(WARNING) << "Receive wrong URL \"" << url << '"';
return string();
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.h
index f7d1e4aaba..9b4e92edce 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/HttpUrl.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,29 +11,54 @@
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
+#include <utility>
+
namespace td {
class HttpUrl {
public:
- enum class Protocol { HTTP, HTTPS } protocol_;
+ enum class Protocol { Http, Https } protocol_ = Protocol::Http;
string userinfo_;
string host_;
- bool is_ipv6;
- int specified_port_;
- int port_;
+ bool is_ipv6_ = false;
+ int specified_port_ = 0;
+ int port_ = 0;
string query_;
string get_url() const;
+
+ HttpUrl(Protocol protocol, string userinfo, string host, bool is_ipv6, int specified_port, int port, string query)
+ : protocol_(protocol)
+ , userinfo_(std::move(userinfo))
+ , host_(std::move(host))
+ , is_ipv6_(is_ipv6)
+ , specified_port_(specified_port)
+ , port_(port)
+ , query_(std::move(query)) {
+ }
};
-// TODO Slice instead of MutableSlice
-Result<HttpUrl> parse_url(MutableSlice url,
- HttpUrl::Protocol default_protocol = HttpUrl::Protocol::HTTP) TD_WARN_UNUSED_RESULT;
+Result<HttpUrl> parse_url(Slice url,
+ HttpUrl::Protocol default_protocol = HttpUrl::Protocol::Http) TD_WARN_UNUSED_RESULT;
StringBuilder &operator<<(StringBuilder &sb, const HttpUrl &url);
+class HttpUrlQuery {
+ public:
+ vector<string> path_;
+ vector<std::pair<string, string>> args_;
+
+ bool has_arg(Slice key) const;
+
+ Slice get_arg(Slice key) const;
+};
+
+HttpUrlQuery parse_url_query(Slice query);
+
+string get_url_host(Slice url);
+
string get_url_query_file_name(const string &query);
-string get_url_file_name(const string &url);
+string get_url_file_name(Slice url);
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.cpp
index eb654f43cd..f5823ad806 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,10 +8,12 @@
#include "td/utils/misc.h"
#include "td/utils/ScopeGuard.h"
+#include "td/utils/SliceBuilder.h"
#include <cstring>
namespace td {
+
StringBuilder &operator<<(StringBuilder &sb, const JsonRawString &val) {
sb << '"';
SCOPE_EXIT {
@@ -94,11 +96,11 @@ StringBuilder &operator<<(StringBuilder &sb, const JsonString &val) {
break;
}
if (128 <= ch) {
- int a = s[pos];
+ uint32 a = ch;
CHECK((a & 0x40) != 0);
CHECK(pos + 1 < len);
- int b = s[++pos];
+ uint32 b = static_cast<unsigned char>(s[++pos]);
CHECK((b & 0xc0) == 0x80);
if ((a & 0x20) == 0) {
CHECK((a & 0x1e) > 0);
@@ -107,7 +109,7 @@ StringBuilder &operator<<(StringBuilder &sb, const JsonString &val) {
}
CHECK(pos + 1 < len);
- int c = s[++pos];
+ uint32 c = static_cast<unsigned char>(s[++pos]);
CHECK((c & 0xc0) == 0x80);
if ((a & 0x10) == 0) {
CHECK(((a & 0x0f) | (b & 0x20)) > 0);
@@ -116,7 +118,7 @@ StringBuilder &operator<<(StringBuilder &sb, const JsonString &val) {
}
CHECK(pos + 1 < len);
- int d = s[++pos];
+ uint32 d = static_cast<unsigned char>(s[++pos]);
CHECK((d & 0xc0) == 0x80);
if ((a & 0x08) == 0) {
CHECK(((a & 0x07) | (b & 0x30)) > 0);
@@ -133,6 +135,7 @@ StringBuilder &operator<<(StringBuilder &sb, const JsonString &val) {
}
return sb;
}
+
Result<MutableSlice> json_string_decode(Parser &parser) {
if (!parser.try_skip('"')) {
return Status::Error("Opening '\"' expected");
@@ -232,7 +235,7 @@ Result<MutableSlice> json_string_decode(Parser &parser) {
} else if (num < 0x800) {
*cur_dest++ = static_cast<char>(0xc0 + (num >> 6));
*cur_dest++ = static_cast<char>(0x80 + (num & 63));
- } else if (num < 0xffff) {
+ } else if (num <= 0xffff) {
*cur_dest++ = static_cast<char>(0xe0 + (num >> 12));
*cur_dest++ = static_cast<char>(0x80 + ((num >> 6) & 63));
*cur_dest++ = static_cast<char>(0x80 + (num & 63));
@@ -341,20 +344,20 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
parser.skip_whitespaces();
switch (parser.peek_char()) {
case 'f':
- if (parser.skip_start_with("false")) {
+ if (parser.try_skip("false")) {
return JsonValue::create_boolean(false);
}
- return Status::Error("Starts with 'f' -- false expected");
+ return Status::Error("Token starts with 'f' -- false expected");
case 't':
- if (parser.skip_start_with("true")) {
+ if (parser.try_skip("true")) {
return JsonValue::create_boolean(true);
}
- return Status::Error("Starts with 't' -- true expected");
+ return Status::Error("Token starts with 't' -- true expected");
case 'n':
- if (parser.skip_start_with("null")) {
+ if (parser.try_skip("null")) {
return JsonValue();
}
- return Status::Error("Starts with 'n' -- null expected");
+ return Status::Error("Token starts with 'n' -- null expected");
case '"': {
TRY_RESULT(slice, json_string_decode(parser));
return JsonValue::create_string(slice);
@@ -368,7 +371,7 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
}
while (true) {
if (parser.empty()) {
- return Status::Error("Unexpected end");
+ return Status::Error("Unexpected string end");
}
TRY_RESULT(value, do_json_decode(parser, max_depth - 1));
res.emplace_back(std::move(value));
@@ -381,20 +384,23 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
parser.skip_whitespaces();
continue;
}
- return Status::Error("Unexpected symbol");
+ if (parser.empty()) {
+ return Status::Error("Unexpected string end");
+ }
+ return Status::Error("Unexpected symbol while parsing JSON Array");
}
return JsonValue::create_array(std::move(res));
}
case '{': {
parser.skip('{');
parser.skip_whitespaces();
- std::vector<std::pair<MutableSlice, JsonValue> > res;
+ std::vector<std::pair<MutableSlice, JsonValue>> res;
if (parser.try_skip('}')) {
return JsonValue::make_object(std::move(res));
}
while (true) {
if (parser.empty()) {
- return Status::Error("Unexpected end");
+ return Status::Error("Unexpected string end");
}
TRY_RESULT(key, json_string_decode(parser));
parser.skip_whitespaces();
@@ -402,7 +408,7 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
return Status::Error("':' expected");
}
TRY_RESULT(value, do_json_decode(parser, max_depth - 1));
- res.emplace_back(std::move(key), std::move(value));
+ res.emplace_back(key, std::move(value));
parser.skip_whitespaces();
if (parser.try_skip('}')) {
@@ -412,7 +418,10 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
parser.skip_whitespaces();
continue;
}
- return Status::Error("Unexpected symbol");
+ if (parser.empty()) {
+ return Status::Error("Unexpected string end");
+ }
+ return Status::Error("Unexpected symbol while parsing JSON Object");
}
return JsonValue::make_object(std::move(res));
}
@@ -434,7 +443,7 @@ Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
return JsonValue::create_number(num);
}
case 0:
- return Status::Error("Unexpected end");
+ return Status::Error("Unexpected string end");
default: {
char next = parser.peek_char();
if (0 < next && next < 127) {
@@ -455,17 +464,17 @@ Status do_json_skip(Parser &parser, int32 max_depth) {
parser.skip_whitespaces();
switch (parser.peek_char()) {
case 'f':
- if (parser.skip_start_with("false")) {
+ if (parser.try_skip("false")) {
return Status::OK();
}
return Status::Error("Starts with 'f' -- false expected");
case 't':
- if (parser.skip_start_with("true")) {
+ if (parser.try_skip("true")) {
return Status::OK();
}
return Status::Error("Starts with 't' -- true expected");
case 'n':
- if (parser.skip_start_with("null")) {
+ if (parser.try_skip("null")) {
return Status::OK();
}
return Status::Error("Starts with 'n' -- null expected");
@@ -576,7 +585,7 @@ Slice JsonValue::get_type_name(Type type) {
}
}
-bool has_json_object_field(JsonObject &object, Slice name) {
+bool has_json_object_field(const JsonObject &object, Slice name) {
for (auto &field_value : object) {
if (field_value.first == name) {
return true;
@@ -585,6 +594,15 @@ bool has_json_object_field(JsonObject &object, Slice name) {
return false;
}
+JsonValue get_json_object_field_force(JsonObject &object, Slice name) {
+ for (auto &field_value : object) {
+ if (field_value.first == name) {
+ return std::move(field_value.second);
+ }
+ }
+ return JsonValue();
+}
+
Result<JsonValue> get_json_object_field(JsonObject &object, Slice name, JsonValue::Type type, bool is_optional) {
for (auto &field_value : object) {
if (field_value.first == name) {
@@ -611,11 +629,41 @@ Result<bool> get_json_object_bool_field(JsonObject &object, Slice name, bool is_
}
Result<int32> get_json_object_int_field(JsonObject &object, Slice name, bool is_optional, int32 default_value) {
- TRY_RESULT(value, get_json_object_field(object, name, JsonValue::Type::Number, is_optional));
- if (value.type() == JsonValue::Type::Null) {
+ for (auto &field_value : object) {
+ if (field_value.first == name) {
+ if (field_value.second.type() == JsonValue::Type::String) {
+ return to_integer_safe<int32>(field_value.second.get_string());
+ }
+ if (field_value.second.type() == JsonValue::Type::Number) {
+ return to_integer_safe<int32>(field_value.second.get_number());
+ }
+
+ return Status::Error(400, PSLICE() << "Field \"" << name << "\" must be of type Number");
+ }
+ }
+ if (is_optional) {
return default_value;
}
- return to_integer_safe<int32>(value.get_number());
+ return Status::Error(400, PSLICE() << "Can't find field \"" << name << "\"");
+}
+
+Result<int64> get_json_object_long_field(JsonObject &object, Slice name, bool is_optional, int64 default_value) {
+ for (auto &field_value : object) {
+ if (field_value.first == name) {
+ if (field_value.second.type() == JsonValue::Type::String) {
+ return to_integer_safe<int64>(field_value.second.get_string());
+ }
+ if (field_value.second.type() == JsonValue::Type::Number) {
+ return to_integer_safe<int64>(field_value.second.get_number());
+ }
+
+ return Status::Error(400, PSLICE() << "Field \"" << name << "\" must be a Number");
+ }
+ }
+ if (is_optional) {
+ return default_value;
+ }
+ return Status::Error(400, PSLICE() << "Can't find field \"" << name << "\"");
}
Result<double> get_json_object_double_field(JsonObject &object, Slice name, bool is_optional, double default_value) {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.h
index 735c4b29ec..90035cd6e2 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/JsonBuilder.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,20 +15,11 @@
#include "td/utils/StringBuilder.h"
#include <new>
-#include <tuple>
#include <type_traits>
#include <utility>
namespace td {
-template <class... Args>
-std::tuple<const Args &...> ctie(const Args &... args) TD_WARN_UNUSED_RESULT;
-
-template <class... Args>
-std::tuple<const Args &...> ctie(const Args &... args) {
- return std::tie(args...);
-}
-
class JsonTrue {
public:
friend StringBuilder &operator<<(StringBuilder &sb, const JsonTrue &val) {
@@ -104,7 +95,7 @@ class JsonFloat {
class JsonOneChar {
public:
- explicit JsonOneChar(unsigned int c) : c_(c) {
+ explicit JsonOneChar(uint32 c) : c_(c) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonOneChar &val) {
@@ -114,12 +105,12 @@ class JsonOneChar {
}
private:
- unsigned int c_;
+ uint32 c_;
};
class JsonChar {
public:
- explicit JsonChar(unsigned int c) : c_(c) {
+ explicit JsonChar(uint32 c) : c_(c) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonChar &val) {
auto c = val.c_;
@@ -138,7 +129,7 @@ class JsonChar {
}
private:
- unsigned int c_;
+ uint32 c_;
};
class JsonRaw {
@@ -181,7 +172,7 @@ class JsonObjectScope;
class JsonBuilder {
public:
- explicit JsonBuilder(StringBuilder &&sb) : sb_(std::move(sb)) {
+ explicit JsonBuilder(StringBuilder &&sb = {}, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) {
}
StringBuilder &string_builder() {
return sb_;
@@ -191,22 +182,48 @@ class JsonBuilder {
JsonArrayScope enter_array() TD_WARN_UNUSED_RESULT;
JsonObjectScope enter_object() TD_WARN_UNUSED_RESULT;
+ int32 offset() const {
+ return offset_;
+ }
+ bool is_pretty() const {
+ return offset_ >= 0;
+ }
+ void print_offset() {
+ if (offset_ >= 0) {
+ sb_ << '\n';
+ for (int x = 0; x < offset_; x++) {
+ sb_ << " ";
+ }
+ }
+ }
+ void dec_offset() {
+ if (offset_ >= 0) {
+ CHECK(offset_ > 0);
+ offset_--;
+ }
+ }
+ void inc_offset() {
+ if (offset_ >= 0) {
+ offset_++;
+ }
+ }
+
private:
StringBuilder sb_;
JsonScope *scope_ = nullptr;
+ int32 offset_;
};
class Jsonable {};
class JsonScope {
public:
- explicit JsonScope(JsonBuilder *jb) : sb_(&jb->sb_), jb_(jb) {
- save_scope_ = jb_->scope_;
+ explicit JsonScope(JsonBuilder *jb) : sb_(&jb->sb_), jb_(jb), save_scope_(jb->scope_) {
jb_->scope_ = this;
CHECK(is_active());
}
JsonScope(const JsonScope &other) = delete;
- JsonScope(JsonScope &&other) : sb_(other.sb_), jb_(other.jb_), save_scope_(other.save_scope_) {
+ JsonScope(JsonScope &&other) noexcept : sb_(other.sb_), jb_(other.jb_), save_scope_(other.save_scope_) {
other.jb_ = nullptr;
}
JsonScope &operator=(const JsonScope &) = delete;
@@ -272,9 +289,7 @@ class JsonScope {
*sb_ << x;
return *this;
}
- JsonScope &operator<<(bool x) {
- return *this << JsonBool(x);
- }
+ JsonScope &operator<<(bool x) = delete;
JsonScope &operator<<(int32 x) {
return *this << JsonInt(x);
}
@@ -284,8 +299,6 @@ class JsonScope {
JsonScope &operator<<(double x) {
return *this << JsonFloat(x);
}
- template <class T>
- JsonScope &operator<<(const T *x); // not implemented
template <size_t N>
JsonScope &operator<<(const char (&x)[N]) {
return *this << JsonString(Slice(x));
@@ -293,15 +306,12 @@ class JsonScope {
JsonScope &operator<<(const char *x) {
return *this << JsonString(Slice(x));
}
- JsonScope &operator<<(const string &x) {
- return *this << JsonString(Slice(x));
- }
JsonScope &operator<<(Slice x) {
return *this << JsonString(x);
}
};
-class JsonValueScope : public JsonScope {
+class JsonValueScope final : public JsonScope {
public:
using JsonScope::JsonScope;
template <class T>
@@ -326,9 +336,10 @@ class JsonValueScope : public JsonScope {
bool was_ = false;
};
-class JsonArrayScope : public JsonScope {
+class JsonArrayScope final : public JsonScope {
public:
explicit JsonArrayScope(JsonBuilder *jb) : JsonScope(jb) {
+ jb->inc_offset();
*sb_ << "[";
}
JsonArrayScope(JsonArrayScope &&other) = default;
@@ -338,10 +349,16 @@ class JsonArrayScope : public JsonScope {
}
}
void leave() {
+ jb_->dec_offset();
+ jb_->print_offset();
*sb_ << "]";
}
template <class T>
JsonArrayScope &operator<<(const T &x) {
+ return (*this)(x);
+ }
+ template <class T>
+ JsonArrayScope &operator()(const T &x) {
enter_value() << x;
return *this;
}
@@ -352,6 +369,7 @@ class JsonArrayScope : public JsonScope {
} else {
is_first_ = true;
}
+ jb_->print_offset();
return jb_->enter_value();
}
@@ -359,9 +377,10 @@ class JsonArrayScope : public JsonScope {
bool is_first_ = false;
};
-class JsonObjectScope : public JsonScope {
+class JsonObjectScope final : public JsonScope {
public:
explicit JsonObjectScope(JsonBuilder *jb) : JsonScope(jb) {
+ jb->inc_offset();
*sb_ << "{";
}
JsonObjectScope(JsonObjectScope &&other) = default;
@@ -371,23 +390,26 @@ class JsonObjectScope : public JsonScope {
}
}
void leave() {
+ jb_->dec_offset();
+ jb_->print_offset();
*sb_ << "}";
}
- template <class S, class T>
- JsonObjectScope &operator<<(std::tuple<S, T> key_value) {
- return *this << std::pair<S, T>(std::get<0>(key_value), std::get<1>(key_value));
- }
- template <class S, class T>
- JsonObjectScope &operator<<(std::pair<S, T> key_value) {
+ template <class T>
+ JsonObjectScope &operator()(Slice key, T &&value) {
CHECK(is_active());
if (is_first_) {
*sb_ << ",";
} else {
is_first_ = true;
}
- jb_->enter_value() << key_value.first;
- *sb_ << ":";
- jb_->enter_value() << key_value.second;
+ jb_->print_offset();
+ jb_->enter_value() << key;
+ if (jb_->is_pretty()) {
+ *sb_ << " : ";
+ } else {
+ *sb_ << ":";
+ }
+ jb_->enter_value() << value;
return *this;
}
JsonObjectScope &operator<<(const JsonRaw &key_value) {
@@ -426,7 +448,7 @@ class JsonValue;
using JsonObject = vector<std::pair<MutableSlice, JsonValue>>;
using JsonArray = vector<JsonValue>;
-class JsonValue : public Jsonable {
+class JsonValue final : private Jsonable {
public:
enum class Type { Null, Number, Boolean, String, Array, Object };
@@ -437,10 +459,10 @@ class JsonValue : public Jsonable {
~JsonValue() {
destroy();
}
- JsonValue(JsonValue &&other) : JsonValue() {
+ JsonValue(JsonValue &&other) noexcept : JsonValue() {
init(std::move(other));
}
- JsonValue &operator=(JsonValue &&other) {
+ JsonValue &operator=(JsonValue &&other) noexcept {
if (&other == this) {
return *this;
}
@@ -557,7 +579,7 @@ class JsonValue : public Jsonable {
case Type::Object: {
auto object = scope->enter_object();
for (auto &key_value : get_object()) {
- object << ctie(JsonString(key_value.first), key_value.second);
+ object(key_value.first, key_value.second);
}
break;
}
@@ -645,25 +667,25 @@ class JsonValue : public Jsonable {
inline StringBuilder &operator<<(StringBuilder &sb, JsonValue::Type type) {
switch (type) {
- case JsonValue::Type::Object:
- return sb << "JsonObject";
- case JsonValue::Type::Boolean:
- return sb << "JsonBoolean";
case JsonValue::Type::Null:
- return sb << "JsonNull";
+ return sb << "Null";
case JsonValue::Type::Number:
- return sb << "JsonNumber";
- case JsonValue::Type::Array:
- return sb << "JsonArray";
+ return sb << "Number";
+ case JsonValue::Type::Boolean:
+ return sb << "Boolean";
case JsonValue::Type::String:
- return sb << "JsonString";
+ return sb << "String";
+ case JsonValue::Type::Array:
+ return sb << "Array";
+ case JsonValue::Type::Object:
+ return sb << "Object";
default:
UNREACHABLE();
return sb;
}
}
-class VirtuallyJsonable : public Jsonable {
+class VirtuallyJsonable : private Jsonable {
public:
virtual void store(JsonValueScope *scope) const = 0;
VirtuallyJsonable() = default;
@@ -674,11 +696,11 @@ class VirtuallyJsonable : public Jsonable {
virtual ~VirtuallyJsonable() = default;
};
-class VirtuallyJsonableInt : public VirtuallyJsonable {
+class VirtuallyJsonableInt final : public VirtuallyJsonable {
public:
explicit VirtuallyJsonableInt(int32 value) : value_(value) {
}
- void store(JsonValueScope *scope) const override {
+ void store(JsonValueScope *scope) const final {
*scope << JsonInt(value_);
}
@@ -686,11 +708,11 @@ class VirtuallyJsonableInt : public VirtuallyJsonable {
int32 value_;
};
-class VirtuallyJsonableLong : public VirtuallyJsonable {
+class VirtuallyJsonableLong final : public VirtuallyJsonable {
public:
explicit VirtuallyJsonableLong(int64 value) : value_(value) {
}
- void store(JsonValueScope *scope) const override {
+ void store(JsonValueScope *scope) const final {
*scope << JsonLong(value_);
}
@@ -698,11 +720,11 @@ class VirtuallyJsonableLong : public VirtuallyJsonable {
int64 value_;
};
-class VirtuallyJsonableString : public VirtuallyJsonable {
+class VirtuallyJsonableString final : public VirtuallyJsonable {
public:
explicit VirtuallyJsonableString(Slice value) : value_(value) {
}
- void store(JsonValueScope *scope) const override {
+ void store(JsonValueScope *scope) const final {
*scope << JsonString(value_);
}
@@ -716,8 +738,8 @@ Status json_string_skip(Parser &parser) TD_WARN_UNUSED_RESULT;
Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
Status do_json_skip(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
-inline Result<JsonValue> json_decode(MutableSlice from) {
- Parser parser(from);
+inline Result<JsonValue> json_decode(MutableSlice json) {
+ Parser parser(json);
const int32 DEFAULT_MAX_DEPTH = 100;
auto result = do_json_decode(parser, DEFAULT_MAX_DEPTH);
if (result.is_ok()) {
@@ -730,17 +752,92 @@ inline Result<JsonValue> json_decode(MutableSlice from) {
}
template <class StrT, class ValT>
-StrT json_encode(const ValT &val) {
- auto buf_len = 1 << 19;
+StrT json_encode(const ValT &val, bool pretty = false) {
+ auto buf_len = 1 << 18;
auto buf = StackAllocator::alloc(buf_len);
- JsonBuilder jb(StringBuilder(buf.as_slice()));
+ JsonBuilder jb(StringBuilder(buf.as_slice(), true), pretty ? 0 : -1);
jb.enter_value() << val;
- LOG_IF(ERROR, jb.string_builder().is_error()) << "Json buffer overflow";
+ if (pretty) {
+ jb.string_builder() << "\n";
+ }
+ LOG_IF(ERROR, jb.string_builder().is_error()) << "JSON buffer overflow";
auto slice = jb.string_builder().as_cslice();
return StrT(slice.begin(), slice.size());
}
-bool has_json_object_field(JsonObject &object, Slice name);
+template <class T>
+class ToJsonImpl final : private Jsonable {
+ public:
+ explicit ToJsonImpl(const T &value) : value_(value) {
+ }
+ void store(JsonValueScope *scope) const {
+ to_json(*scope, value_);
+ }
+
+ private:
+ const T &value_;
+};
+
+template <class T>
+auto ToJson(const T &value) {
+ return ToJsonImpl<T>(value);
+}
+
+template <class T>
+void to_json(JsonValueScope &jv, const T &value) {
+ jv << value;
+}
+
+template <class F>
+class JsonObjectImpl : private Jsonable {
+ public:
+ explicit JsonObjectImpl(F &&f) : f_(std::forward<F>(f)) {
+ }
+ void store(JsonValueScope *scope) const {
+ auto object = scope->enter_object();
+ f_(object);
+ }
+
+ private:
+ F f_;
+};
+
+template <class F>
+auto json_object(F &&f) {
+ return JsonObjectImpl<F>(std::forward<F>(f));
+}
+
+template <class F>
+class JsonArrayImpl : private Jsonable {
+ public:
+ explicit JsonArrayImpl(F &&f) : f_(std::forward<F>(f)) {
+ }
+ void store(JsonValueScope *scope) const {
+ auto array = scope->enter_array();
+ f_(array);
+ }
+
+ private:
+ F f_;
+};
+
+template <class F>
+auto json_array(F &&f) {
+ return JsonArrayImpl<F>(std::forward<F>(f));
+}
+
+template <class A, class F>
+auto json_array(const A &a, F &&f) {
+ return json_array([&a, &f](auto &arr) {
+ for (auto &x : a) {
+ arr(f(x));
+ }
+ });
+}
+
+bool has_json_object_field(const JsonObject &object, Slice name);
+
+JsonValue get_json_object_field_force(JsonObject &object, Slice name) TD_WARN_UNUSED_RESULT;
Result<JsonValue> get_json_object_field(JsonObject &object, Slice name, JsonValue::Type type,
bool is_optional = true) TD_WARN_UNUSED_RESULT;
@@ -751,6 +848,9 @@ Result<bool> get_json_object_bool_field(JsonObject &object, Slice name, bool is_
Result<int32> get_json_object_int_field(JsonObject &object, Slice name, bool is_optional = true,
int32 default_value = 0) TD_WARN_UNUSED_RESULT;
+Result<int64> get_json_object_long_field(JsonObject &object, Slice name, bool is_optional = true,
+ int64 default_value = 0) TD_WARN_UNUSED_RESULT;
+
Result<double> get_json_object_double_field(JsonObject &object, Slice name, bool is_optional = true,
double default_value = 0.0) TD_WARN_UNUSED_RESULT;
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/List.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/List.h
index 1606c44d2b..4f9bb9877f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/List.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/List.h
@@ -1,12 +1,12 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/logging.h"
+#include "td/utils/common.h"
namespace td {
@@ -24,23 +24,23 @@ struct ListNode {
ListNode(const ListNode &) = delete;
ListNode &operator=(const ListNode &) = delete;
- ListNode(ListNode &&other) {
+ ListNode(ListNode &&other) noexcept {
if (other.empty()) {
clear();
} else {
- ListNode *head = other.prev;
- other.remove();
- head->put(this);
+ init_from(std::move(other));
}
}
- ListNode &operator=(ListNode &&other) {
+ ListNode &operator=(ListNode &&other) noexcept {
+ if (this == &other) {
+ return *this;
+ }
+
this->remove();
if (!other.empty()) {
- ListNode *head = other.prev;
- other.remove();
- head->put(this);
+ init_from(std::move(other));
}
return *this;
@@ -58,11 +58,12 @@ struct ListNode {
}
void put(ListNode *other) {
- other->connect(next);
- this->connect(other);
+ DCHECK(other->empty());
+ put_unsafe(other);
}
void put_back(ListNode *other) {
+ DCHECK(other->empty());
prev->connect(other);
other->connect(this);
}
@@ -82,11 +83,47 @@ struct ListNode {
return next == this;
}
- private:
+ ListNode *begin() {
+ return next;
+ }
+ ListNode *end() {
+ return this;
+ }
+ const ListNode *begin() const {
+ return next;
+ }
+ const ListNode *end() const {
+ return this;
+ }
+ ListNode *get_next() {
+ return next;
+ }
+ ListNode *get_prev() {
+ return prev;
+ }
+ const ListNode *get_next() const {
+ return next;
+ }
+ const ListNode *get_prev() const {
+ return prev;
+ }
+
+ protected:
void clear() {
next = this;
prev = this;
}
+
+ void init_from(ListNode &&other) {
+ ListNode *head = other.prev;
+ other.remove();
+ head->put_unsafe(this);
+ }
+
+ void put_unsafe(ListNode *other) {
+ other->connect(next);
+ this->connect(other);
+ }
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MapNode.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MapNode.h
new file mode 100644
index 0000000000..cad2ae9b36
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MapNode.h
@@ -0,0 +1,166 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/HashTableUtils.h"
+
+#include <new>
+#include <type_traits>
+#include <utility>
+
+namespace td {
+
+template <class KeyT, class ValueT, class Enable = void>
+struct MapNode {
+ using first_type = KeyT;
+ using second_type = ValueT;
+ using public_key_type = KeyT;
+ using public_type = MapNode;
+
+ KeyT first{};
+ union {
+ ValueT second;
+ };
+
+ const KeyT &key() const {
+ return first;
+ }
+
+ MapNode &get_public() {
+ return *this;
+ }
+
+ const MapNode &get_public() const {
+ return *this;
+ }
+
+ MapNode() {
+ }
+ MapNode(KeyT key, ValueT value) : first(std::move(key)) {
+ new (&second) ValueT(std::move(value));
+ DCHECK(!empty());
+ }
+ MapNode(const MapNode &other) = delete;
+ MapNode &operator=(const MapNode &other) = delete;
+ MapNode(MapNode &&other) noexcept {
+ *this = std::move(other);
+ }
+ void operator=(MapNode &&other) noexcept {
+ DCHECK(empty());
+ DCHECK(!other.empty());
+ first = std::move(other.first);
+ other.first = KeyT();
+ new (&second) ValueT(std::move(other.second));
+ other.second.~ValueT();
+ }
+ ~MapNode() {
+ if (!empty()) {
+ second.~ValueT();
+ }
+ }
+
+ void copy_from(const MapNode &other) {
+ DCHECK(empty());
+ DCHECK(!other.empty());
+ first = other.first;
+ new (&second) ValueT(other.second);
+ }
+
+ bool empty() const {
+ return is_hash_table_key_empty(first);
+ }
+
+ void clear() {
+ DCHECK(!empty());
+ first = KeyT();
+ second.~ValueT();
+ DCHECK(empty());
+ }
+
+ template <class... ArgsT>
+ void emplace(KeyT key, ArgsT &&...args) {
+ DCHECK(empty());
+ first = std::move(key);
+ new (&second) ValueT(std::forward<ArgsT>(args)...);
+ DCHECK(!empty());
+ }
+};
+
+template <class KeyT, class ValueT>
+struct MapNode<KeyT, ValueT, typename std::enable_if_t<(sizeof(KeyT) + sizeof(ValueT) > 28 * sizeof(void *))>> {
+ struct Impl {
+ using first_type = KeyT;
+ using second_type = ValueT;
+
+ KeyT first{};
+ union {
+ ValueT second;
+ };
+
+ template <class InputKeyT, class... ArgsT>
+ Impl(InputKeyT &&key, ArgsT &&...args) : first(std::forward<InputKeyT>(key)) {
+ new (&second) ValueT(std::forward<ArgsT>(args)...);
+ DCHECK(!is_hash_table_key_empty(first));
+ }
+ Impl(const Impl &other) = delete;
+ Impl &operator=(const Impl &other) = delete;
+ Impl(Impl &&other) = delete;
+ void operator=(Impl &&other) = delete;
+ ~Impl() {
+ second.~ValueT();
+ }
+ };
+
+ using first_type = KeyT;
+ using second_type = ValueT;
+ using public_key_type = KeyT;
+ using public_type = Impl;
+
+ unique_ptr<Impl> impl_;
+
+ const KeyT &key() const {
+ DCHECK(!empty());
+ return impl_->first;
+ }
+
+ Impl &get_public() {
+ return *impl_;
+ }
+
+ const Impl &get_public() const {
+ return *impl_;
+ }
+
+ MapNode() {
+ }
+ MapNode(KeyT key, ValueT value) : impl_(td::make_unique<Impl>(std::move(key), std::move(value))) {
+ }
+
+ void copy_from(const MapNode &other) {
+ DCHECK(empty());
+ DCHECK(!other.empty());
+ impl_ = td::make_unique<Impl>(other.impl_->first, other.impl_->second);
+ }
+
+ bool empty() const {
+ return impl_ == nullptr;
+ }
+
+ void clear() {
+ DCHECK(!empty());
+ impl_ = nullptr;
+ }
+
+ template <class... ArgsT>
+ void emplace(KeyT key, ArgsT &&...args) {
+ DCHECK(empty());
+ impl_ = td::make_unique<Impl>(std::move(key), std::forward<ArgsT>(args)...);
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MemoryLog.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MemoryLog.h
index aa125df2f7..04b03b60ee 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MemoryLog.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MemoryLog.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -17,15 +17,27 @@
namespace td {
template <int buffer_size = 32 * (1 << 10)>
-class MemoryLog : public LogInterface {
+class MemoryLog final : public LogInterface {
static constexpr size_t MAX_OUTPUT_SIZE = buffer_size / 16 < (8 << 10) ? buffer_size / 16 : (8 << 10);
+ static_assert((buffer_size & (buffer_size - 1)) == 0, "Buffer size must be power of 2");
+ static_assert(buffer_size >= (8 << 10), "Too small buffer size");
+
public:
MemoryLog() {
std::memset(buffer_, ' ', sizeof(buffer_));
}
- void append(CSlice new_slice, int log_level) override {
+ Slice get_buffer() const {
+ return Slice(buffer_, sizeof(buffer_));
+ }
+
+ size_t get_pos() const {
+ return pos_ & (buffer_size - 1);
+ }
+
+ private:
+ void do_append(int log_level, CSlice new_slice) final {
Slice slice = new_slice;
slice.truncate(MAX_OUTPUT_SIZE);
while (!slice.empty() && slice.back() == '\n') {
@@ -34,50 +46,34 @@ class MemoryLog : public LogInterface {
size_t slice_size = slice.size();
CHECK(slice_size * 3 < buffer_size);
size_t pad_size = ((slice_size + 15) & ~15) - slice_size;
- constexpr size_t magic_size = 16;
- uint32 total_size = static_cast<uint32>(slice_size + pad_size + magic_size);
- uint32 real_pos = pos_.fetch_add(total_size, std::memory_order_relaxed);
+ constexpr size_t MAGIC_SIZE = 16;
+ auto total_size = static_cast<uint32>(slice_size + pad_size + MAGIC_SIZE);
+ auto real_pos = pos_.fetch_add(total_size, std::memory_order_relaxed);
CHECK((total_size & 15) == 0);
uint32 start_pos = real_pos & (buffer_size - 1);
uint32 end_pos = start_pos + total_size;
if (likely(end_pos <= buffer_size)) {
- std::memcpy(&buffer_[start_pos + magic_size], slice.data(), slice_size);
- std::memcpy(&buffer_[start_pos + magic_size + slice_size], " ", pad_size);
+ std::memcpy(&buffer_[start_pos + MAGIC_SIZE], slice.data(), slice_size);
+ std::memcpy(&buffer_[start_pos + MAGIC_SIZE + slice_size], " ", pad_size);
} else {
- size_t first = buffer_size - start_pos - magic_size;
+ size_t first = buffer_size - start_pos - MAGIC_SIZE;
size_t second = slice_size - first;
- std::memcpy(&buffer_[start_pos + magic_size], slice.data(), first);
+ std::memcpy(&buffer_[start_pos + MAGIC_SIZE], slice.data(), first);
std::memcpy(&buffer_[0], slice.data() + first, second);
- std::memcpy(&buffer_[second], " ", pad_size);
+ std::memcpy(&buffer_[second], " ", pad_size);
}
CHECK((start_pos & 15) == 0);
- CHECK(start_pos <= buffer_size - magic_size);
+ CHECK(start_pos <= buffer_size - MAGIC_SIZE);
buffer_[start_pos] = '\n';
- size_t printed = std::snprintf(&buffer_[start_pos + 1], magic_size - 1, "LOG:%08x: ", real_pos);
- CHECK(printed == magic_size - 2);
- buffer_[start_pos + magic_size - 1] = ' ';
-
- if (log_level == VERBOSITY_NAME(FATAL)) {
- process_fatal_error(new_slice);
- }
+ size_t printed = std::snprintf(&buffer_[start_pos + 1], MAGIC_SIZE - 1, "LOG:%08x: ", real_pos);
+ CHECK(printed == MAGIC_SIZE - 2);
+ buffer_[start_pos + MAGIC_SIZE - 1] = ' ';
}
- void rotate() override {
- }
-
- Slice get_buffer() const {
- return Slice(buffer_, sizeof(buffer_));
- }
-
- size_t get_pos() const {
- return pos_ & (buffer_size - 1);
- }
-
- private:
char buffer_[buffer_size];
- std::atomic<uint32> pos_;
+ std::atomic<uint32> pos_{0};
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.cpp
index 75c4fe34b5..a7dde2405f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.h
index 11210ceb30..ccaf029a52 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MimeType.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MovableValue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MovableValue.h
index 939bf51f28..7a8ef459e0 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MovableValue.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MovableValue.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,16 +14,18 @@ class MovableValue {
MovableValue() = default;
MovableValue(T val) : val_(val) {
}
- MovableValue(MovableValue &&other) : val_(other.val_) {
+ MovableValue(MovableValue &&other) noexcept : val_(other.val_) {
other.clear();
}
- MovableValue &operator=(MovableValue &&other) {
- val_ = other.val_;
- other.clear();
+ MovableValue &operator=(MovableValue &&other) noexcept {
+ if (this != &other) {
+ val_ = other.val_;
+ other.clear();
+ }
return *this;
}
- MovableValue(const MovableValue &) = delete;
- MovableValue &operator=(const MovableValue &) = delete;
+ MovableValue(const MovableValue &) = default;
+ MovableValue &operator=(const MovableValue &) = default;
~MovableValue() = default;
void clear() {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.cpp
new file mode 100644
index 0000000000..1fcbc04341
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.cpp
@@ -0,0 +1,15 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/MpmcQueue.h"
+
+namespace td {
+namespace detail {
+
+MpmcStat stat_;
+
+} // namespace detail
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.h
index ae65554b72..f1f12a37f4 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcQueue.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,7 +14,7 @@
#include "td/utils/format.h"
#include "td/utils/HazardPointers.h"
#include "td/utils/logging.h"
-#include "td/utils/port/thread.h"
+#include "td/utils/port/sleep.h"
#include "td/utils/ScopeGuard.h"
#include <array>
@@ -23,6 +23,7 @@
namespace td {
namespace detail {
+
struct MpmcStat {
void alloc_ok(size_t thread_id) {
s(thread_id).alloc_ok_cnt++;
@@ -64,8 +65,10 @@ struct MpmcStat {
return arr[thread_id];
}
};
+
+extern MpmcStat stat_;
+
} // namespace detail
-//detail::MpmcStat stat_;
template <class T>
class OneValue {
@@ -95,31 +98,35 @@ class OneValue {
private:
enum Type : int { Empty = 0, Taken, Value };
std::atomic<int> state_{Empty};
- T value_;
+ T value_{};
};
template <class T>
class OneValue<T *> {
public:
bool set_value(T *value) {
- T *was = nullptr;
+ T *was = Empty();
return state_.compare_exchange_strong(was, value, std::memory_order_acq_rel);
}
bool get_value(T *&value) {
value = state_.exchange(Taken(), std::memory_order_acq_rel);
- return value != nullptr;
+ return value != Empty();
}
void reset() {
- state_ = nullptr;
+ state_ = Empty();
}
OneValue() {
}
private:
- std::atomic<T *> state_{nullptr};
- T *Taken() {
- static T xxx;
- return &xxx;
+ std::atomic<T *> state_{Empty()};
+ static T *Empty() {
+ static int64 xxx;
+ return reinterpret_cast<T *>(&xxx);
+ }
+ static T *Taken() {
+ static int64 xxx;
+ return reinterpret_cast<T *>(&xxx);
}
};
@@ -149,6 +156,11 @@ class MpmcQueueBlock {
//returns Ok, Empty or Closed
PopStatus try_pop(T &value) {
while (true) {
+ // this check slows 1:1 case but prevents writer starvation in 1:N case
+ if (write_pos_.load(std::memory_order_relaxed) <= read_pos_.load(std::memory_order_relaxed) &&
+ read_pos_.load(std::memory_order_relaxed) < nodes_.size()) {
+ return PopStatus::Empty;
+ }
auto read_pos = read_pos_.fetch_add(1, std::memory_order_relaxed);
if (read_pos >= nodes_.size()) {
return PopStatus::Closed;
@@ -199,7 +211,7 @@ class MpmcQueueOld {
return "Mpmc queue (fetch and add array queue)";
}
MpmcQueueOld(size_t block_size, size_t threads_n) : block_size_{block_size}, hazard_pointers_{threads_n} {
- auto node = std::make_unique<Node>(block_size_);
+ auto node = make_unique<Node>(block_size_);
write_pos_ = node.get();
read_pos_ = node.get();
node.release();
@@ -217,7 +229,7 @@ class MpmcQueueOld {
delete to_delete;
}
//stat_.dump();
- //stat_ = MpmcStat();
+ //stat_ = detail::MpmcStat();
}
size_t hazard_pointers_to_delele_size_unsafe() const {
@@ -231,7 +243,7 @@ class MpmcQueueOld {
using PopStatus = typename MpmcQueueBlock<T>::PopStatus;
void push(T value, size_t thread_id) {
- auto hazard_ptr_holder = hazard_pointers_.get_holder(thread_id, 0);
+ typename decltype(hazard_pointers_)::Holder hazard_ptr_holder(hazard_pointers_, thread_id, 0);
while (true) {
auto node = hazard_ptr_holder.protect(write_pos_);
auto status = node->block.push(value);
@@ -263,7 +275,7 @@ class MpmcQueueOld {
}
bool try_pop(T &value, size_t thread_id) {
- auto hazard_ptr_holder = hazard_pointers_.get_holder(thread_id, 0);
+ typename decltype(hazard_pointers_)::Holder hazard_ptr_holder(hazard_pointers_, thread_id, 0);
while (true) {
auto node = hazard_ptr_holder.protect(read_pos_);
auto status = node->block.try_pop(value);
@@ -293,7 +305,7 @@ class MpmcQueueOld {
if (try_pop(value, thread_id)) {
return value;
}
- td::this_thread::yield();
+ usleep_for(1);
}
}
@@ -306,9 +318,9 @@ class MpmcQueueOld {
MpmcQueueBlock<T> block;
//Got pad in MpmcQueueBlock
};
- std::atomic<Node *> write_pos_;
+ std::atomic<Node *> write_pos_{nullptr};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
- std::atomic<Node *> read_pos_;
+ std::atomic<Node *> read_pos_{nullptr};
char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
size_t block_size_;
HazardPointers<Node, 1> hazard_pointers_;
@@ -324,7 +336,7 @@ class MpmcQueue {
return "NEW Mpmc queue (fetch and add array queue)";
}
MpmcQueue(size_t block_size, size_t threads_n) : hazard_pointers_{threads_n} {
- auto node = std::make_unique<Node>();
+ auto node = make_unique<Node>();
write_pos_ = node.get();
read_pos_ = node.get();
node.release();
@@ -417,7 +429,7 @@ class MpmcQueue {
if (try_pop(value, thread_id)) {
return value;
}
- td::this_thread::yield();
+ usleep_for(1);
}
}
@@ -438,9 +450,9 @@ class MpmcQueue {
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
//Got pad in MpmcQueueBlock
};
- std::atomic<Node *> write_pos_;
+ std::atomic<Node *> write_pos_{nullptr};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
- std::atomic<Node *> read_pos_;
+ std::atomic<Node *> read_pos_{nullptr};
char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
HazardPointers<Node, 1> hazard_pointers_;
//Got pad in HazardPointers
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcWaiter.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcWaiter.h
index 0f48620e63..7ece498ff6 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcWaiter.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpmcWaiter.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,61 +7,81 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/port/thread.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/sleep.h"
+#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <mutex>
namespace td {
-class MpmcWaiter {
+class MpmcEagerWaiter {
public:
- int wait(int yields, uint32 worker_id) {
- if (yields < RoundsTillSleepy) {
- td::this_thread::yield();
- return yields + 1;
- } else if (yields == RoundsTillSleepy) {
+ struct Slot {
+ private:
+ friend class MpmcEagerWaiter;
+ int yields;
+ uint32 worker_id;
+ };
+ static void init_slot(Slot &slot, uint32 worker_id) {
+ slot.yields = 0;
+ slot.worker_id = worker_id;
+ }
+
+ void wait(Slot &slot) {
+ if (slot.yields < RoundsTillSleepy) {
+ yield();
+ slot.yields++;
+ } else if (slot.yields == RoundsTillSleepy) {
auto state = state_.load(std::memory_order_relaxed);
if (!State::has_worker(state)) {
- auto new_state = State::with_worker(state, worker_id);
- if (state_.compare_exchange_strong(state, new_state)) {
- td::this_thread::yield();
- return yields + 1;
+ auto new_state = State::with_worker(state, slot.worker_id);
+ if (state_.compare_exchange_strong(state, new_state, std::memory_order_acq_rel)) {
+ yield();
+ slot.yields++;
+ return;
}
if (state == State::awake()) {
- return 0;
+ slot.yields = 0;
+ return;
}
}
- td::this_thread::yield();
- return 0;
- } else if (yields < RoundsTillAsleep) {
+ yield();
+ slot.yields = 0;
+ } else if (slot.yields < RoundsTillAsleep) {
auto state = state_.load(std::memory_order_acquire);
- if (State::still_sleepy(state, worker_id)) {
- td::this_thread::yield();
- return yields + 1;
+ if (State::still_sleepy(state, slot.worker_id)) {
+ yield();
+ slot.yields++;
+ return;
}
- return 0;
+ slot.yields = 0;
} else {
auto state = state_.load(std::memory_order_acquire);
- if (State::still_sleepy(state, worker_id)) {
+ if (State::still_sleepy(state, slot.worker_id)) {
std::unique_lock<std::mutex> lock(mutex_);
if (state_.compare_exchange_strong(state, State::asleep(), std::memory_order_acq_rel)) {
condition_variable_.wait(lock);
}
}
- return 0;
+ slot.yields = 0;
}
}
- int stop_wait(int yields, uint32 worker_id) {
- if (yields > RoundsTillSleepy) {
+ void stop_wait(Slot &slot) {
+ if (slot.yields > RoundsTillSleepy) {
notify_cold();
}
- return 0;
+ slot.yields = 0;
+ }
+
+ void close() {
}
void notify() {
+ std::atomic_thread_fence(std::memory_order_seq_cst);
if (state_.load(std::memory_order_acquire) == State::awake()) {
return;
}
@@ -90,6 +110,7 @@ class MpmcWaiter {
}
};
enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
+ // enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
std::atomic<uint32> state_{State::awake()};
std::mutex mutex_;
std::condition_variable condition_variable_;
@@ -101,6 +122,216 @@ class MpmcWaiter {
condition_variable_.notify_all();
}
}
+ static void yield() {
+ // whatever, this is better than sched_yield
+ usleep_for(1);
+ }
};
+class MpmcSleepyWaiter {
+ public:
+ struct Slot {
+ private:
+ friend class MpmcSleepyWaiter;
+
+ enum State { Search, Work, Sleep } state_{Work};
+
+ void park() {
+ std::unique_lock<std::mutex> guard(mutex_);
+ condition_variable_.wait(guard, [&] { return unpark_flag_; });
+ unpark_flag_ = false;
+ }
+
+ bool cancel_park() {
+ auto res = unpark_flag_;
+ unpark_flag_ = false;
+ return res;
+ }
+
+ void unpark() {
+ //TODO: try to unlock guard before notify_all
+ std::unique_lock<std::mutex> guard(mutex_);
+ unpark_flag_ = true;
+ condition_variable_.notify_all();
+ }
+
+ std::mutex mutex_;
+ std::condition_variable condition_variable_;
+ bool unpark_flag_{false}; // TODO: move out of lock
+ int yield_cnt{0};
+ int32 worker_id{0};
+
+ public:
+ char padding[TD_CONCURRENCY_PAD];
+ };
+
+ // There are a lot of workers
+ // Each has a slot
+ //
+ // States of a worker:
+ // - searching for work | Search
+ // - processing work | Work
+ // - sleeping | Sleep
+ //
+ // When somebody adds a work it calls notify
+ //
+ // notify
+ // if there are workers in search phase do nothing.
+ // if all workers are awake do nothing
+ // otherwise wake some random worker
+ //
+ // Initially all workers are in Search mode.
+ //
+ // When worker found nothing it may try to call wait.
+ // This may put it in a Sleep for some time.
+ // After wait returns worker will be in Search state again.
+ //
+ // Suppose worker found a work and ready to process it.
+ // Then it may call stop_wait. This will cause transition from
+ // Search to Work state.
+ //
+ // Main invariant:
+ // After notify is called there should be at least on worker in Search or Work state.
+ // If possible - in Search state
+ //
+
+ static void init_slot(Slot &slot, int32 worker_id) {
+ slot.state_ = Slot::State::Work;
+ slot.unpark_flag_ = false;
+ slot.worker_id = worker_id;
+ VLOG(waiter) << "Init slot " << worker_id;
+ }
+
+ static constexpr int VERBOSITY_NAME(waiter) = VERBOSITY_NAME(DEBUG) + 10;
+ void wait(Slot &slot) {
+ if (slot.state_ == Slot::State::Work) {
+ VLOG(waiter) << "Work -> Search";
+ state_++;
+ slot.state_ = Slot::State::Search;
+ slot.yield_cnt = 0;
+ return;
+ }
+ if (slot.state_ == Slot::Search) {
+ if (slot.yield_cnt++ < 10 && false) {
+ // TODO some sleep backoff is possible
+ return;
+ }
+
+ slot.state_ = Slot::State::Sleep;
+ std::unique_lock<std::mutex> guard(sleepers_mutex_);
+ auto state_view = StateView(state_.fetch_add((1 << PARKING_SHIFT) - 1));
+ CHECK(state_view.searching_count != 0);
+ bool should_search = state_view.searching_count == 1;
+ if (closed_) {
+ return;
+ }
+ sleepers_.push_back(&slot);
+ LOG_CHECK(slot.unpark_flag_ == false) << slot.worker_id;
+ VLOG(waiter) << "Add to sleepers " << slot.worker_id;
+ //guard.unlock();
+ if (should_search) {
+ VLOG(waiter) << "Search -> Search once, then Sleep ";
+ return;
+ }
+ VLOG(waiter) << "Search -> Sleep " << state_view.searching_count << " " << state_view.parked_count;
+ }
+
+ CHECK(slot.state_ == Slot::State::Sleep);
+ VLOG(waiter) << "Park " << slot.worker_id;
+ slot.park();
+ VLOG(waiter) << "Resume " << slot.worker_id;
+ slot.state_ = Slot::State::Search;
+ slot.yield_cnt = 0;
+ }
+
+ void stop_wait(Slot &slot) {
+ if (slot.state_ == Slot::State::Work) {
+ return;
+ }
+ if (slot.state_ == Slot::State::Sleep) {
+ VLOG(waiter) << "Search once, then Sleep -> Work/Search " << slot.worker_id;
+ slot.state_ = Slot::State::Work;
+ std::unique_lock<std::mutex> guard(sleepers_mutex_);
+ auto it = std::find(sleepers_.begin(), sleepers_.end(), &slot);
+ if (it != sleepers_.end()) {
+ sleepers_.erase(it);
+ VLOG(waiter) << "Remove from sleepers " << slot.worker_id;
+ state_.fetch_sub((1 << PARKING_SHIFT) - 1);
+ guard.unlock();
+ } else {
+ guard.unlock();
+ VLOG(waiter) << "Not in sleepers" << slot.worker_id;
+ CHECK(slot.cancel_park());
+ }
+ }
+ VLOG(waiter) << "Search once, then Sleep -> Work " << slot.worker_id;
+ slot.state_ = Slot::State::Search;
+ auto state_view = StateView(state_.fetch_sub(1));
+ CHECK(state_view.searching_count != 0);
+ CHECK(state_view.searching_count < 1000);
+ bool should_notify = state_view.searching_count == 1;
+ if (should_notify) {
+ VLOG(waiter) << "Notify others";
+ notify();
+ }
+ VLOG(waiter) << "Search -> Work ";
+ slot.state_ = Slot::State::Work;
+ }
+
+ void notify() {
+ auto view = StateView(state_.load());
+ //LOG(ERROR) << view.parked_count;
+ if (view.searching_count > 0 || view.parked_count == 0) {
+ VLOG(waiter) << "Ingore notify: " << view.searching_count << " " << view.parked_count;
+ return;
+ }
+
+ VLOG(waiter) << "Notify: " << view.searching_count << " " << view.parked_count;
+ std::unique_lock<std::mutex> guard(sleepers_mutex_);
+
+ view = StateView(state_.load());
+ if (view.searching_count > 0) {
+ VLOG(waiter) << "Skip notify: got searching";
+ return;
+ }
+
+ CHECK(view.parked_count == static_cast<int>(sleepers_.size()));
+ if (sleepers_.empty()) {
+ VLOG(waiter) << "Skip notify: no sleepers";
+ return;
+ }
+
+ auto sleeper = sleepers_.back();
+ sleepers_.pop_back();
+ state_.fetch_sub((1 << PARKING_SHIFT) - 1);
+ VLOG(waiter) << "Unpark " << sleeper->worker_id;
+ sleeper->unpark();
+ }
+
+ void close() {
+ StateView state(state_.load());
+ LOG_CHECK(state.parked_count == 0) << state.parked_count;
+ LOG_CHECK(state.searching_count == 0) << state.searching_count;
+ }
+
+ private:
+ static constexpr int32 PARKING_SHIFT = 16;
+ struct StateView {
+ int32 parked_count;
+ int32 searching_count;
+ explicit StateView(int32 x) {
+ parked_count = x >> PARKING_SHIFT;
+ searching_count = x & ((1 << PARKING_SHIFT) - 1);
+ }
+ };
+ std::atomic<int32> state_{0};
+
+ std::mutex sleepers_mutex_;
+ vector<Slot *> sleepers_;
+
+ bool closed_ = false;
+};
+
+using MpmcWaiter = MpmcSleepyWaiter;
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscLinkQueue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscLinkQueue.h
index 4398c7503d..a1b05c2791 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscLinkQueue.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscLinkQueue.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,6 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include <atomic>
@@ -149,17 +148,17 @@ template <class Value>
class MpscLinkQueueUniquePtrNode {
public:
MpscLinkQueueUniquePtrNode() = default;
- explicit MpscLinkQueueUniquePtrNode(std::unique_ptr<Value> ptr) : ptr_(std::move(ptr)) {
+ explicit MpscLinkQueueUniquePtrNode(unique_ptr<Value> ptr) : ptr_(std::move(ptr)) {
}
MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
return ptr_.release()->to_mpsc_link_queue_node();
}
- static MpscLinkQueueUniquePtrNode<Value> from_mpsc_link_queue_node(td::MpscLinkQueueImpl::Node *node) {
- return MpscLinkQueueUniquePtrNode<Value>(std::unique_ptr<Value>(Value::from_mpsc_link_queue_node(node)));
+ static MpscLinkQueueUniquePtrNode<Value> from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
+ return MpscLinkQueueUniquePtrNode<Value>(unique_ptr<Value>(Value::from_mpsc_link_queue_node(node)));
}
- explicit operator bool() {
+ explicit operator bool() const noexcept {
return ptr_ != nullptr;
}
@@ -168,7 +167,7 @@ class MpscLinkQueueUniquePtrNode {
}
private:
- std::unique_ptr<Value> ptr_;
+ unique_ptr<Value> ptr_;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscPollableQueue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscPollableQueue.h
index 89d2df8693..f6de4bf280 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscPollableQueue.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/MpscPollableQueue.h
@@ -1,57 +1,63 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+#include "td/utils/common.h"
#include "td/utils/misc.h"
#include "td/utils/port/EventFd.h"
-#include "td/utils/SpinLock.h"
#if !TD_EVENTFD_UNSUPPORTED
-#if !TD_WINDOWS
-#include <poll.h>
-#include <sched.h>
-#endif
+
+#include "td/utils/port/Mutex.h"
#include <utility>
namespace td {
// interface like in PollableQueue
-template <class ValueT>
+template <class T>
class MpscPollableQueue {
public:
+ using ValueType = T;
+
int reader_wait_nonblock() {
auto ready = reader_vector_.size() - reader_pos_;
if (ready != 0) {
return narrow_cast<int>(ready);
}
- auto guard = lock_.lock();
- if (writer_vector_.empty()) {
+ for (int i = 0; i < 2; i++) {
+ auto guard = lock_.lock();
+ if (writer_vector_.empty()) {
+ if (i == 1) {
+ wait_event_fd_ = true;
+ return 0;
+ }
+ } else {
+ reader_vector_.clear();
+ reader_pos_ = 0;
+ std::swap(writer_vector_, reader_vector_);
+ return narrow_cast<int>(reader_vector_.size());
+ }
event_fd_.acquire();
- wait_event_fd_ = true;
- return 0;
- } else {
- reader_vector_.clear();
- reader_pos_ = 0;
- std::swap(writer_vector_, reader_vector_);
- return narrow_cast<int>(reader_vector_.size());
}
+ UNREACHABLE();
}
- ValueT reader_get_unsafe() {
+ ValueType reader_get_unsafe() {
return std::move(reader_vector_[reader_pos_++]);
}
void reader_flush() {
//nop
}
- void writer_put(ValueT value) {
+ void writer_put(ValueType value) {
auto guard = lock_.lock();
writer_vector_.push_back(std::move(value));
if (wait_event_fd_) {
wait_event_fd_ = false;
+ guard.reset();
event_fd_.release();
}
}
@@ -62,6 +68,11 @@ class MpscPollableQueue {
//nop
}
+ bool is_empty() {
+ auto guard = lock_.lock();
+ return writer_vector_.empty() && reader_vector_.empty();
+ }
+
void init() {
event_fd_.init();
}
@@ -75,35 +86,27 @@ class MpscPollableQueue {
}
}
-// Just example of usage
-#if !TD_WINDOWS
+ // Just an example of usage
int reader_wait() {
int res;
-
while ((res = reader_wait_nonblock()) == 0) {
- // TODO: reader_flush?
- pollfd fd;
- fd.fd = reader_get_event_fd().get_fd().get_native_fd();
- fd.events = POLLIN;
- poll(&fd, 1, -1);
+ reader_get_event_fd().wait(1000);
}
return res;
}
-#endif
private:
- SpinLock lock_;
+ Mutex lock_;
bool wait_event_fd_{false};
EventFd event_fd_;
- std::vector<ValueT> writer_vector_;
- std::vector<ValueT> reader_vector_;
+ std::vector<ValueType> writer_vector_;
+ std::vector<ValueType> reader_vector_;
size_t reader_pos_{0};
};
} // namespace td
#else
-#include "td/utils/logging.h"
namespace td {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Named.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Named.h
index 202de5f7d4..251f14b86d 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Named.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Named.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/NullLog.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/NullLog.h
new file mode 100644
index 0000000000..71b9ad7d7c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/NullLog.h
@@ -0,0 +1,19 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/logging.h"
+#include "td/utils/Slice.h"
+
+namespace td {
+
+class NullLog final : public LogInterface {
+ void do_append(int /*log_level*/, CSlice /*slice*/) final {
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ObjectPool.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ObjectPool.h
index e6e4549dbb..8ee2a58566 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/ObjectPool.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ObjectPool.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -45,7 +45,7 @@ class ObjectPool {
// It is not very usual case of acquire/release use.
// Instead of publishing an object via some flag we do the opposite.
// We publish new generation via destruction of the data.
- // In usual case if we see a flag then we are able to use an object.
+ // In usual case if we see a flag, then we are able to use an object.
// In our case if we have used an object and it is already invalid, then generation will mismatch
bool is_alive() const {
if (!storage_) {
@@ -84,11 +84,11 @@ class ObjectPool {
OwnerPtr() = default;
OwnerPtr(const OwnerPtr &) = delete;
OwnerPtr &operator=(const OwnerPtr &) = delete;
- OwnerPtr(OwnerPtr &&other) : storage_(other.storage_), parent_(other.parent_) {
+ OwnerPtr(OwnerPtr &&other) noexcept : storage_(other.storage_), parent_(other.parent_) {
other.storage_ = nullptr;
other.parent_ = nullptr;
}
- OwnerPtr &operator=(OwnerPtr &&other) {
+ OwnerPtr &operator=(OwnerPtr &&other) noexcept {
if (this != &other) {
storage_ = other.storage_;
parent_ = other.parent_;
@@ -156,7 +156,7 @@ class ObjectPool {
};
template <class... ArgsT>
- OwnerPtr create(ArgsT &&... args) {
+ OwnerPtr create(ArgsT &&...args) {
Storage *storage = get_storage();
storage->init_data(std::forward<ArgsT>(args)...);
return OwnerPtr(storage, this);
@@ -189,7 +189,7 @@ class ObjectPool {
delete to_delete;
storage_count_--;
}
- CHECK(storage_count_.load() == 0) << storage_count_.load();
+ LOG_CHECK(storage_count_.load() == 0) << storage_count_.load();
}
private:
@@ -201,7 +201,7 @@ class ObjectPool {
std::atomic<int32> generation{1};
template <class... ArgsT>
- void init_data(ArgsT &&... args) {
+ void init_data(ArgsT &&...args) {
// new (&data) DataT(std::forward<ArgsT>(args)...);
data = DataT(std::forward<ArgsT>(args)...);
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Observer.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Observer.h
index 8511e0ce8b..33e8bc1a4c 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Observer.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Observer.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -22,13 +22,13 @@ class ObserverBase {
virtual void notify() = 0;
};
-class Observer : ObserverBase {
+class Observer final : private ObserverBase {
public:
Observer() = default;
explicit Observer(unique_ptr<ObserverBase> &&ptr) : observer_ptr_(std::move(ptr)) {
}
- void notify() override {
+ void notify() final {
if (observer_ptr_) {
observer_ptr_->notify();
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.cpp
new file mode 100644
index 0000000000..76571d4954
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.cpp
@@ -0,0 +1,263 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/OptionParser.h"
+
+#include "td/utils/FlatHashMap.h"
+#include "td/utils/logging.h"
+#include "td/utils/PathView.h"
+#include "td/utils/SliceBuilder.h"
+
+#if TD_PORT_WINDOWS
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#include "td/utils/port/wstring_convert.h"
+#endif
+#endif
+
+#if TD_PORT_WINDOWS
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#include <shellapi.h>
+#endif
+#endif
+
+namespace td {
+
+void OptionParser::set_usage(Slice executable_name, Slice usage) {
+ PathView path_view(executable_name);
+ usage_ = PSTRING() << path_view.file_name() << " " << usage;
+}
+
+void OptionParser::set_description(string description) {
+ description_ = std::move(description);
+}
+
+void OptionParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
+ std::function<Status(Slice)> callback) {
+ for (auto &option : options_) {
+ if ((short_key != '\0' && option.short_key == short_key) || (!long_key.empty() && long_key == option.long_key)) {
+ LOG(ERROR) << "Ignore duplicated option '" << (short_key == '\0' ? '-' : short_key) << "' '" << long_key << "'";
+ }
+ }
+ options_.push_back(Option{type, short_key, long_key.str(), description.str(), std::move(callback)});
+}
+
+void OptionParser::add_checked_option(char short_key, Slice long_key, Slice description,
+ std::function<Status(Slice)> callback) {
+ add_option(Option::Type::Arg, short_key, long_key, description, std::move(callback));
+}
+
+void OptionParser::add_checked_option(char short_key, Slice long_key, Slice description,
+ std::function<Status(void)> callback) {
+ add_option(Option::Type::NoArg, short_key, long_key, description,
+ [callback = std::move(callback)](Slice) { return callback(); });
+}
+
+void OptionParser::add_option(char short_key, Slice long_key, Slice description, std::function<void(Slice)> callback) {
+ add_option(Option::Type::Arg, short_key, long_key, description, [callback = std::move(callback)](Slice parameter) {
+ callback(parameter);
+ return Status::OK();
+ });
+}
+
+void OptionParser::add_option(char short_key, Slice long_key, Slice description, std::function<void(void)> callback) {
+ add_option(Option::Type::NoArg, short_key, long_key, description, [callback = std::move(callback)](Slice) {
+ callback();
+ return Status::OK();
+ });
+}
+
+void OptionParser::add_check(std::function<Status()> check) {
+ checks_.push_back(std::move(check));
+}
+
+Result<vector<char *>> OptionParser::run(int argc, char *argv[], int expected_non_option_count) {
+#if TD_PORT_WINDOWS
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+ LPWSTR *utf16_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
+ if (utf16_argv == nullptr) {
+ return Status::Error("Failed to parse command line");
+ }
+ vector<string> args_storage(argc);
+ vector<char *> args(argc);
+ for (int i = 0; i < argc; i++) {
+ TRY_RESULT_ASSIGN(args_storage[i], from_wstring(utf16_argv[i]));
+ args[i] = &args_storage[i][0];
+ }
+ LocalFree(utf16_argv);
+ argv = &args[0];
+#endif
+#endif
+
+ return run_impl(argc, argv, expected_non_option_count);
+}
+
+Result<vector<char *>> OptionParser::run_impl(int argc, char *argv[], int expected_non_option_count) {
+ FlatHashMap<char, const Option *> short_options;
+ FlatHashMap<string, const Option *> long_options;
+ for (auto &opt : options_) {
+ if (opt.short_key != '\0') {
+ short_options[opt.short_key] = &opt;
+ }
+ if (!opt.long_key.empty()) {
+ long_options[opt.long_key] = &opt;
+ }
+ }
+
+ vector<char *> non_options;
+ for (int arg_pos = 1; arg_pos < argc; arg_pos++) {
+ const char *arg = argv[arg_pos];
+ if (arg[0] != '-' || arg[1] == '\0') {
+ non_options.push_back(argv[arg_pos]);
+ continue;
+ }
+ if (arg[1] == '-' && arg[2] == '\0') {
+ // "--"; after it everything is non-option
+ while (++arg_pos < argc) {
+ non_options.push_back(argv[arg_pos]);
+ }
+ break;
+ }
+
+ if (arg[1] == '-') {
+ // long option
+ Slice long_arg(arg + 2);
+ Slice parameter;
+ auto equal_pos = long_arg.find('=');
+ bool has_equal = equal_pos != Slice::npos;
+ if (has_equal) {
+ parameter = long_arg.substr(equal_pos + 1);
+ long_arg = long_arg.substr(0, equal_pos);
+ }
+
+ auto it = long_options.find(long_arg.str());
+ if (it == long_options.end()) {
+ return Status::Error(PSLICE() << "Option \"" << long_arg << "\" is unrecognized");
+ }
+
+ auto option = it->second;
+ switch (option->type) {
+ case Option::Type::NoArg:
+ if (has_equal) {
+ return Status::Error(PSLICE() << "Option \"" << long_arg << "\" must not have an argument");
+ }
+ break;
+ case Option::Type::Arg:
+ if (!has_equal) {
+ if (++arg_pos == argc) {
+ return Status::Error(PSLICE() << "Option \"" << long_arg << "\" requires an argument");
+ }
+ parameter = Slice(argv[arg_pos]);
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ TRY_STATUS(option->arg_callback(parameter));
+ continue;
+ }
+
+ for (size_t opt_pos = 1; arg[opt_pos] != '\0'; opt_pos++) {
+ auto it = short_options.find(arg[opt_pos]);
+ if (it == short_options.end()) {
+ return Status::Error(PSLICE() << "Option \"" << arg[opt_pos] << "\" is unrecognized");
+ }
+
+ auto option = it->second;
+ Slice parameter;
+ switch (option->type) {
+ case Option::Type::NoArg:
+ // nothing to do
+ break;
+ case Option::Type::Arg:
+ if (arg[opt_pos + 1] == '\0') {
+ if (++arg_pos == argc) {
+ return Status::Error(PSLICE() << "Option \"" << arg[opt_pos] << "\" requires an argument");
+ }
+ parameter = Slice(argv[arg_pos]);
+ } else {
+ parameter = Slice(arg + opt_pos + 1);
+ opt_pos += parameter.size();
+ }
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ TRY_STATUS(option->arg_callback(parameter));
+ }
+ }
+ if (expected_non_option_count >= 0 && non_options.size() != static_cast<size_t>(expected_non_option_count)) {
+ if (expected_non_option_count == 0) {
+ return Status::Error("Unexpected non-option parameters specified");
+ }
+ if (non_options.size() > static_cast<size_t>(expected_non_option_count)) {
+ return Status::Error("Too many non-option parameters specified");
+ } else {
+ return Status::Error("Too few non-option parameters specified");
+ }
+ }
+ for (auto &check : checks_) {
+ TRY_STATUS(check());
+ }
+
+ return std::move(non_options);
+}
+
+StringBuilder &operator<<(StringBuilder &sb, const OptionParser &o) {
+ if (!o.usage_.empty()) {
+ sb << "Usage: " << o.usage_ << "\n\n";
+ }
+ if (!o.description_.empty()) {
+ sb << o.description_ << ". ";
+ }
+ sb << "Options:\n";
+
+ size_t max_length = 0;
+ for (auto &opt : o.options_) {
+ size_t length = 2;
+ if (!opt.long_key.empty()) {
+ length += 4 + opt.long_key.size();
+ }
+ if (opt.type != OptionParser::Option::Type::NoArg) {
+ length += 6;
+ }
+ if (length > max_length) {
+ max_length = length;
+ }
+ }
+ max_length++;
+
+ for (auto &opt : o.options_) {
+ bool has_short_key = opt.short_key != '\0';
+ sb << " ";
+ size_t length = max_length;
+ if (has_short_key) {
+ sb << '-' << opt.short_key;
+ } else {
+ sb << " ";
+ }
+ length -= 2;
+ if (!opt.long_key.empty()) {
+ if (has_short_key) {
+ sb << ", ";
+ } else {
+ sb << " ";
+ }
+ sb << "--" << opt.long_key;
+ length -= 4 + opt.long_key.size();
+ }
+ if (opt.type != OptionParser::Option::Type::NoArg) {
+ sb << "=<arg>";
+ length -= 6;
+ }
+ sb << string(length, ' ') << opt.description;
+ sb << '\n';
+ }
+ return sb;
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.h
new file mode 100644
index 0000000000..82d4296dca
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionParser.h
@@ -0,0 +1,81 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/misc.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+#include "td/utils/StringBuilder.h"
+
+#include <functional>
+
+namespace td {
+
+class OptionParser {
+ class Option {
+ public:
+ enum class Type { NoArg, Arg };
+ Type type;
+ char short_key;
+ string long_key;
+ string description;
+ std::function<Status(Slice)> arg_callback;
+ };
+
+ void add_option(Option::Type type, char short_key, Slice long_key, Slice description,
+ std::function<Status(Slice)> callback);
+
+ public:
+ template <class T>
+ static std::function<Status(Slice)> parse_integer(T &value) {
+ return [&value](Slice value_str) {
+ TRY_RESULT_ASSIGN(value, to_integer_safe<T>(value_str));
+ return Status::OK();
+ };
+ }
+
+ static std::function<void(Slice)> parse_string(string &value) {
+ return [&value](Slice value_str) {
+ value = value_str.str();
+ };
+ }
+
+ void set_usage(Slice executable_name, Slice usage);
+
+ void set_description(string description);
+
+ void add_checked_option(char short_key, Slice long_key, Slice description, std::function<Status(Slice)> callback);
+
+ void add_checked_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback);
+
+ void add_option(char short_key, Slice long_key, Slice description, std::function<Status(Slice)> callback) = delete;
+
+ void add_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback) = delete;
+
+ void add_option(char short_key, Slice long_key, Slice description, std::function<void(Slice)> callback);
+
+ void add_option(char short_key, Slice long_key, Slice description, std::function<void(void)> callback);
+
+ void add_check(std::function<Status()> check);
+
+ // returns found non-option parameters
+ Result<vector<char *>> run(int argc, char *argv[], int expected_non_option_count = -1) TD_WARN_UNUSED_RESULT;
+
+ // for testing only
+ Result<vector<char *>> run_impl(int argc, char *argv[], int expected_non_option_count) TD_WARN_UNUSED_RESULT;
+
+ friend StringBuilder &operator<<(StringBuilder &sb, const OptionParser &o);
+
+ private:
+ vector<Option> options_;
+ vector<std::function<Status()>> checks_;
+ string usage_;
+ string description_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionsParser.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionsParser.h
deleted file mode 100644
index 6ac0385575..0000000000
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/OptionsParser.h
+++ /dev/null
@@ -1,150 +0,0 @@
-//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-#pragma once
-
-#include "td/utils/common.h"
-#include "td/utils/logging.h"
-#include "td/utils/Slice.h"
-#include "td/utils/Status.h"
-#include "td/utils/StringBuilder.h"
-
-#include <functional>
-#include <string>
-
-#if !TD_WINDOWS
-#include <getopt.h>
-#endif
-
-namespace td {
-
-class OptionsParser {
- public:
- class Option {
- public:
- enum Type { NoArg, Arg, OptionalArg };
- Type type;
- char short_key;
- std::string long_key;
- std::string description;
- std::function<Status(Slice)> arg_callback;
- };
-
- void set_description(std::string description) {
- description_ = std::move(description);
- }
-
- void add_option(Option::Type type, char short_key, Slice long_key, Slice description,
- std::function<Status(Slice)> callback) {
- options_.push_back(Option{type, short_key, long_key.str(), description.str(), std::move(callback)});
- }
-
- void add_option(char short_key, Slice long_key, Slice description, std::function<Status(Slice)> callback) {
- add_option(Option::Type::Arg, short_key, long_key, description, std::move(callback));
- }
-
- void add_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback) {
- // Ouch. There must be some better way
- add_option(Option::Type::NoArg, short_key, long_key, description,
- std::bind([](std::function<Status(void)> &func, Slice) { return func(); }, std::move(callback),
- std::placeholders::_1));
- }
-
- Result<int> run(int argc, char *argv[]) TD_WARN_UNUSED_RESULT {
-#if TD_WINDOWS
- return -1;
-#else
- // use getopt. long keys are not supported for now
- char buff[1024];
- StringBuilder sb({buff, sizeof(buff)});
- for (auto &opt : options_) {
- CHECK(opt.type != Option::OptionalArg);
- sb << opt.short_key;
- if (opt.type == Option::Arg) {
- sb << ":";
- }
- }
- if (sb.is_error()) {
- return Status::Error("Can't parse options");
- }
- CSlice short_options = sb.as_cslice();
-
- vector<option> long_options;
- for (auto &opt : options_) {
- if (opt.long_key.empty()) {
- continue;
- }
- option o;
- o.flag = nullptr;
- o.val = opt.short_key;
- o.has_arg = opt.type == Option::Arg ? required_argument : no_argument;
- o.name = opt.long_key.c_str();
- long_options.push_back(o);
- }
- long_options.push_back({nullptr, 0, nullptr, 0});
-
- while (true) {
- int opt_i = getopt_long(argc, argv, short_options.c_str(), &long_options[0], nullptr);
- if (opt_i == ':') {
- return Status::Error("Missing argument");
- }
- if (opt_i == '?') {
- return Status::Error("Unrecognized option");
- }
- if (opt_i == -1) {
- break;
- }
- bool found = false;
- for (auto &opt : options_) {
- if (opt.short_key == opt_i) {
- Slice arg;
- if (opt.type == Option::Arg) {
- arg = Slice(optarg);
- }
- auto status = opt.arg_callback(arg);
- if (status.is_error()) {
- return std::move(status);
- }
- found = true;
- break;
- }
- }
- if (!found) {
- return Status::Error("Unknown argument");
- }
- }
- return optind;
-#endif
- }
-
- friend StringBuilder &operator<<(StringBuilder &sb, const OptionsParser &o) {
- sb << o.description_ << "\n";
- for (auto &opt : o.options_) {
- sb << "-" << opt.short_key;
- if (!opt.long_key.empty()) {
- sb << "|--" << opt.long_key;
- }
- if (opt.type == Option::OptionalArg) {
- sb << "[";
- }
- if (opt.type != Option::NoArg) {
- sb << "<arg>";
- }
- if (opt.type == Option::OptionalArg) {
- sb << "]";
- }
- sb << "\t" << opt.description;
- sb << "\n";
- }
- return sb;
- }
-
- private:
- std::vector<Option> options_;
- std::string description_;
-};
-
-} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/OrderedEventsProcessor.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/OrderedEventsProcessor.h
index 4515b74684..8b3474ab57 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/OrderedEventsProcessor.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/OrderedEventsProcessor.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -23,9 +23,21 @@ class OrderedEventsProcessor {
explicit OrderedEventsProcessor(SeqNo offset) : offset_(offset), begin_(offset_), end_(offset_) {
}
+ template <class FunctionT>
+ void clear(FunctionT &&function) {
+ for (auto &it : data_array_) {
+ if (it.second) {
+ function(std::move(it.first));
+ }
+ }
+ *this = OrderedEventsProcessor();
+ }
+ void clear() {
+ *this = OrderedEventsProcessor();
+ }
template <class FromDataT, class FunctionT>
void add(SeqNo seq_no, FromDataT &&data, FunctionT &&function) {
- CHECK(seq_no >= begin_) << seq_no << ">=" << begin_; // or ignore?
+ LOG_CHECK(seq_no >= begin_) << seq_no << ">=" << begin_; // or ignore?
if (seq_no == begin_) { // run now
begin_++;
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Parser.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Parser.h
index 06e95bf807..731f885171 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Parser.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Parser.h
@@ -1,14 +1,14 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/format.h"
-#include "td/utils/logging.h"
+#include "td/utils/common.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include <cstring>
@@ -16,14 +16,17 @@
namespace td {
-class Parser {
+namespace detail {
+
+template <class SliceT>
+class ParserImpl {
public:
- explicit Parser(MutableSlice data) : ptr_(data.begin()), end_(data.end()), status_() {
+ explicit ParserImpl(SliceT data) : ptr_(data.begin()), end_(data.end()), status_() {
}
- Parser(Parser &&other) : ptr_(other.ptr_), end_(other.end_), status_(std::move(other.status_)) {
+ ParserImpl(ParserImpl &&other) noexcept : ptr_(other.ptr_), end_(other.end_), status_(std::move(other.status_)) {
other.clear();
}
- Parser &operator=(Parser &&other) {
+ ParserImpl &operator=(ParserImpl &&other) noexcept {
if (&other == this) {
return *this;
}
@@ -33,70 +36,70 @@ class Parser {
other.clear();
return *this;
}
- Parser(const Parser &) = delete;
- Parser &operator=(const Parser &) = delete;
- ~Parser() = default;
+ ParserImpl(const ParserImpl &) = delete;
+ ParserImpl &operator=(const ParserImpl &) = delete;
+ ~ParserImpl() = default;
bool empty() const {
return ptr_ == end_;
}
void clear() {
- ptr_ = nullptr;
+ ptr_ = SliceT().begin();
end_ = ptr_;
status_ = Status::OK();
}
- MutableSlice read_till_nofail(char c) {
+ SliceT read_till_nofail(char c) {
if (status_.is_error()) {
- return MutableSlice();
+ return SliceT();
}
- char *till = reinterpret_cast<char *>(std::memchr(ptr_, c, end_ - ptr_));
+ auto till = static_cast<decltype(ptr_)>(std::memchr(ptr_, c, end_ - ptr_));
if (till == nullptr) {
till = end_;
}
- MutableSlice result(ptr_, till);
+ SliceT result(ptr_, till);
ptr_ = till;
return result;
}
- MutableSlice read_till_nofail(Slice str) {
+ SliceT read_till_nofail(Slice str) {
if (status_.is_error()) {
- return MutableSlice();
+ return SliceT();
}
- char *best_till = end_;
+ auto best_till = end_;
for (auto c : str) {
- char *till = reinterpret_cast<char *>(std::memchr(ptr_, c, end_ - ptr_));
+ auto till = static_cast<decltype(ptr_)>(std::memchr(ptr_, c, end_ - ptr_));
if (till != nullptr && till < best_till) {
best_till = till;
}
}
- MutableSlice result(ptr_, best_till);
+ SliceT result(ptr_, best_till);
ptr_ = best_till;
return result;
}
template <class F>
- MutableSlice read_while(const F &f) {
+ SliceT read_while(const F &f) {
auto save_ptr = ptr_;
while (ptr_ != end_ && f(*ptr_)) {
ptr_++;
}
- return MutableSlice(save_ptr, ptr_);
+ return SliceT(save_ptr, ptr_);
}
- MutableSlice read_all() {
+ SliceT read_all() {
auto save_ptr = ptr_;
ptr_ = end_;
- return MutableSlice(save_ptr, ptr_);
+ return SliceT(save_ptr, ptr_);
}
- MutableSlice read_till(char c) {
+ SliceT read_till(char c) {
if (status_.is_error()) {
- return MutableSlice();
+ return SliceT();
}
- MutableSlice res = read_till_nofail(c);
+ SliceT res = read_till_nofail(c);
if (ptr_ == end_ || ptr_[0] != c) {
- status_ = Status::Error(PSLICE() << "Read till " << tag("char", c) << " failed");
- return MutableSlice();
+ status_ = Status::Error(PSLICE() << "Read till '" << c << "' failed");
+ return SliceT();
}
return res;
}
@@ -122,7 +125,7 @@ class Parser {
return;
}
if (ptr_ == end_ || ptr_[0] != c) {
- status_ = Status::Error(PSLICE() << "Skip " << tag("char", c) << " failed");
+ status_ = Status::Error(PSLICE() << "Skip '" << c << "' failed");
return;
}
ptr_++;
@@ -135,6 +138,14 @@ class Parser {
return false;
}
+ bool try_skip(Slice prefix) {
+ if (prefix.size() > static_cast<size_t>(end_ - ptr_) || prefix != Slice(ptr_, prefix.size())) {
+ return false;
+ }
+ advance(prefix.size());
+ return true;
+ }
+
void skip_till_not(Slice str) {
while (ptr_ != end_) {
if (std::memchr(str.data(), *ptr_, str.size()) == nullptr) {
@@ -146,38 +157,33 @@ class Parser {
void skip_whitespaces() {
skip_till_not(" \t\r\n");
}
+ SliceT read_word() {
+ skip_whitespaces();
+ return read_till_nofail(" \t\r\n");
+ }
- MutableSlice data() const {
- return MutableSlice(ptr_, end_);
+ SliceT data() const {
+ return SliceT(ptr_, end_);
}
Status &status() {
return status_;
}
- bool start_with(Slice prefix) {
- if (prefix.size() + ptr_ > end_) {
- return false;
- }
- return std::memcmp(prefix.begin(), ptr_, prefix.size()) == 0;
- }
-
- bool skip_start_with(Slice prefix) {
- if (start_with(prefix)) {
- advance(prefix.size());
- return true;
- }
- return false;
- }
-
void advance(size_t diff) {
ptr_ += diff;
CHECK(ptr_ <= end_);
}
private:
- char *ptr_;
- char *end_;
+ decltype(std::declval<SliceT>().begin()) ptr_;
+ decltype(std::declval<SliceT>().end()) end_;
Status status_;
};
+
+} // namespace detail
+
+using Parser = detail::ParserImpl<MutableSlice>;
+using ConstParser = detail::ParserImpl<Slice>;
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.cpp
new file mode 100644
index 0000000000..943d8a8c84
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.cpp
@@ -0,0 +1,70 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/PathView.h"
+
+#include "td/utils/common.h"
+#include "td/utils/misc.h"
+
+namespace td {
+
+PathView::PathView(Slice path) : path_(path) {
+ last_slash_ = narrow_cast<int32>(path_.size()) - 1;
+ while (last_slash_ >= 0 && !is_slash(path_[last_slash_])) {
+ last_slash_--;
+ }
+
+ last_dot_ = static_cast<int32>(path_.size());
+ for (auto i = last_dot_ - 1; i > last_slash_ + 1; i--) {
+ if (path_[i] == '.') {
+ last_dot_ = i;
+ break;
+ }
+ }
+}
+
+Slice PathView::parent_dir_noslash() const {
+ if (last_slash_ < 0) {
+ return Slice(".");
+ }
+ if (last_slash_ == 0) {
+ static char buf[1];
+ buf[0] = TD_DIR_SLASH;
+ return Slice(buf, 1);
+ }
+ return path_.substr(0, last_slash_);
+}
+
+Slice PathView::relative(Slice path, Slice dir, bool force) {
+ if (begins_with(path, dir)) {
+ path.remove_prefix(dir.size());
+ return path;
+ }
+ if (force) {
+ return Slice();
+ }
+ return path;
+}
+
+Slice PathView::dir_and_file(Slice path) {
+ auto last_slash = static_cast<int32>(path.size()) - 1;
+ while (last_slash >= 0 && !is_slash(path[last_slash])) {
+ last_slash--;
+ }
+ if (last_slash < 0) {
+ return Slice();
+ }
+ last_slash--;
+ while (last_slash >= 0 && !is_slash(path[last_slash])) {
+ last_slash--;
+ }
+ if (last_slash < 0) {
+ return Slice();
+ }
+ return path.substr(last_slash + 1);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.h
index edb5d7c127..1bafc1d038 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/PathView.h
@@ -1,32 +1,18 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/misc.h"
#include "td/utils/Slice.h"
namespace td {
class PathView {
public:
- explicit PathView(Slice path) : path_(path) {
- last_slash_ = narrow_cast<int32>(path_.size()) - 1;
- while (last_slash_ >= 0 && !is_slash(path_[last_slash_])) {
- last_slash_--;
- }
-
- last_dot_ = static_cast<int32>(path_.size());
- for (auto i = last_dot_ - 1; i > last_slash_ + 1; i--) {
- if (path_[i] == '.') {
- last_dot_ = i;
- break;
- }
- }
- }
+ explicit PathView(Slice path);
bool empty() const {
return path_.empty();
@@ -40,8 +26,9 @@ class PathView {
}
Slice parent_dir() const {
- return Slice(path_.begin(), last_slash_ + 1);
+ return path_.substr(0, last_slash_ + 1);
}
+ Slice parent_dir_noslash() const;
Slice extension() const {
if (last_dot_ == static_cast<int32>(path_.size())) {
@@ -51,7 +38,7 @@ class PathView {
}
Slice without_extension() const {
- return Slice(path_.begin(), last_dot_);
+ return path_.substr(0, last_dot_);
}
Slice file_stem() const {
@@ -62,6 +49,10 @@ class PathView {
return path_.substr(last_slash_ + 1);
}
+ Slice file_name_without_extension() const {
+ return path_.substr(last_slash_ + 1, last_dot_ - last_slash_ - 1);
+ }
+
Slice path() const {
return path_;
}
@@ -74,34 +65,8 @@ class PathView {
return !is_absolute();
}
- static Slice relative(Slice path, Slice dir, bool force = false) {
- if (begins_with(path, dir)) {
- path.remove_prefix(dir.size());
- return path;
- }
- if (force) {
- return Slice();
- }
- return path;
- }
-
- static Slice dir_and_file(Slice path) {
- auto last_slash = static_cast<int32>(path.size()) - 1;
- while (last_slash >= 0 && !is_slash(path[last_slash])) {
- last_slash--;
- }
- if (last_slash < 0) {
- return Slice();
- }
- last_slash--;
- while (last_slash >= 0 && !is_slash(path[last_slash])) {
- last_slash--;
- }
- if (last_slash < 0) {
- return Slice();
- }
- return path.substr(last_slash + 1);
- }
+ static Slice relative(Slice path, Slice dir, bool force = false);
+ static Slice dir_and_file(Slice path);
private:
static bool is_slash(char c) {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Promise.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Promise.h
new file mode 100644
index 0000000000..4e83045d61
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Promise.h
@@ -0,0 +1,373 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/CancellationToken.h"
+#include "td/utils/common.h"
+#include "td/utils/invoke.h"
+#include "td/utils/MovableValue.h"
+#include "td/utils/Status.h"
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace td {
+
+template <class T = Unit>
+class PromiseInterface {
+ public:
+ PromiseInterface() = default;
+ PromiseInterface(const PromiseInterface &) = delete;
+ PromiseInterface &operator=(const PromiseInterface &) = delete;
+ PromiseInterface(PromiseInterface &&) = default;
+ PromiseInterface &operator=(PromiseInterface &&) = default;
+ virtual ~PromiseInterface() = default;
+
+ virtual void set_value(T &&value) {
+ set_result(std::move(value));
+ }
+ virtual void set_error(Status &&error) {
+ set_result(std::move(error));
+ }
+ virtual void set_result(Result<T> &&result) {
+ if (result.is_ok()) {
+ set_value(result.move_as_ok());
+ } else {
+ set_error(result.move_as_error());
+ }
+ }
+
+ virtual bool is_cancellable() const {
+ return false;
+ }
+ virtual bool is_canceled() const {
+ return false;
+ }
+};
+
+template <class T>
+class SafePromise;
+
+template <class T = Unit>
+class Promise;
+
+namespace detail {
+
+template <typename T>
+struct GetArg final : public GetArg<decltype(&T::operator())> {};
+
+template <class C, class R, class Arg>
+class GetArg<R (C::*)(Arg)> {
+ public:
+ using type = Arg;
+};
+template <class C, class R, class Arg>
+class GetArg<R (C::*)(Arg) const> {
+ public:
+ using type = Arg;
+};
+
+template <class T>
+using get_arg_t = std::decay_t<typename GetArg<T>::type>;
+
+template <class T>
+struct DropResult {
+ using type = T;
+};
+
+template <class T>
+struct DropResult<Result<T>> {
+ using type = T;
+};
+
+template <class T>
+using drop_result_t = typename DropResult<T>::type;
+
+template <class ValueT, class FunctionT>
+class LambdaPromise : public PromiseInterface<ValueT> {
+ enum class State : int32 { Empty, Ready, Complete };
+
+ public:
+ void set_value(ValueT &&value) override {
+ CHECK(state_.get() == State::Ready);
+ do_ok(std::move(value));
+ state_ = State::Complete;
+ }
+
+ void set_error(Status &&error) override {
+ if (state_.get() == State::Ready) {
+ do_error(std::move(error));
+ state_ = State::Complete;
+ }
+ }
+ LambdaPromise(const LambdaPromise &other) = delete;
+ LambdaPromise &operator=(const LambdaPromise &other) = delete;
+ LambdaPromise(LambdaPromise &&other) = default;
+ LambdaPromise &operator=(LambdaPromise &&other) = default;
+ ~LambdaPromise() override {
+ if (state_.get() == State::Ready) {
+ do_error(Status::Error("Lost promise"));
+ }
+ }
+
+ template <class FromT>
+ explicit LambdaPromise(FromT &&func) : func_(std::forward<FromT>(func)), state_(State::Ready) {
+ }
+
+ private:
+ FunctionT func_;
+ MovableValue<State> state_{State::Empty};
+
+ template <class F = FunctionT>
+ std::enable_if_t<is_callable<F, Result<ValueT>>::value, void> do_error(Status &&status) {
+ func_(Result<ValueT>(std::move(status)));
+ }
+ template <class Y, class F = FunctionT>
+ std::enable_if_t<!is_callable<F, Result<ValueT>>::value, void> do_error(Y &&status) {
+ func_(Auto());
+ }
+ template <class F = FunctionT>
+ std::enable_if_t<is_callable<F, Result<ValueT>>::value, void> do_ok(ValueT &&value) {
+ func_(Result<ValueT>(std::move(value)));
+ }
+ template <class F = FunctionT>
+ std::enable_if_t<!is_callable<F, Result<ValueT>>::value, void> do_ok(ValueT &&value) {
+ func_(std::move(value));
+ }
+};
+
+template <class T>
+struct is_promise_interface : std::false_type {};
+
+template <class U>
+struct is_promise_interface<PromiseInterface<U>> : std::true_type {};
+
+template <class U>
+struct is_promise_interface<Promise<U>> : std::true_type {};
+
+template <class T>
+struct is_promise_interface_ptr : std::false_type {};
+
+template <class U>
+struct is_promise_interface_ptr<unique_ptr<U>> : std::true_type {};
+
+template <class T = void, class F = void, std::enable_if_t<std::is_same<T, void>::value, bool> has_t = false>
+auto lambda_promise(F &&f) {
+ return LambdaPromise<drop_result_t<get_arg_t<std::decay_t<F>>>, std::decay_t<F>>(std::forward<F>(f));
+}
+template <class T = void, class F = void, std::enable_if_t<!std::is_same<T, void>::value, bool> has_t = true>
+auto lambda_promise(F &&f) {
+ return LambdaPromise<T, std::decay_t<F>>(std::forward<F>(f));
+}
+
+template <class T, class F,
+ std::enable_if_t<is_promise_interface<std::decay_t<F>>::value, bool> from_promise_interface = true>
+auto &&promise_interface(F &&f) {
+ return std::forward<F>(f);
+}
+
+template <class T, class F,
+ std::enable_if_t<!is_promise_interface<std::decay_t<F>>::value, bool> from_promise_interface = false>
+auto promise_interface(F &&f) {
+ return lambda_promise<T>(std::forward<F>(f));
+}
+
+template <class T, class F,
+ std::enable_if_t<is_promise_interface_ptr<std::decay_t<F>>::value, bool> from_promise_interface = true>
+auto promise_interface_ptr(F &&f) {
+ return std::forward<F>(f);
+}
+
+template <class T, class F,
+ std::enable_if_t<!is_promise_interface_ptr<std::decay_t<F>>::value, bool> from_promise_interface = false>
+auto promise_interface_ptr(F &&f) {
+ return td::make_unique<std::decay_t<decltype(promise_interface<T>(std::forward<F>(f)))>>(
+ promise_interface<T>(std::forward<F>(f)));
+}
+} // namespace detail
+
+template <class T>
+class Promise {
+ public:
+ void set_value(T &&value) {
+ if (!promise_) {
+ return;
+ }
+ promise_->set_value(std::move(value));
+ promise_.reset();
+ }
+ void set_error(Status &&error) {
+ if (!promise_) {
+ return;
+ }
+ promise_->set_error(std::move(error));
+ promise_.reset();
+ }
+ void set_result(Result<T> &&result) {
+ if (!promise_) {
+ return;
+ }
+ promise_->set_result(std::move(result));
+ promise_.reset();
+ }
+ void reset() {
+ promise_.reset();
+ }
+ bool is_cancellable() const {
+ if (!promise_) {
+ return false;
+ }
+ return promise_->is_cancellable();
+ }
+ bool is_canceled() const {
+ if (!promise_) {
+ return false;
+ }
+ return promise_->is_canceled();
+ }
+ unique_ptr<PromiseInterface<T>> release() {
+ return std::move(promise_);
+ }
+
+ Promise() = default;
+ explicit Promise(unique_ptr<PromiseInterface<T>> promise) : promise_(std::move(promise)) {
+ }
+ Promise(Auto) {
+ }
+ Promise(SafePromise<T> &&other);
+ Promise &operator=(SafePromise<T> &&other);
+ template <class F, std::enable_if_t<!std::is_same<std::decay_t<F>, Promise>::value, int> = 0>
+ Promise(F &&f) : promise_(detail::promise_interface_ptr<T>(std::forward<F>(f))) {
+ }
+
+ explicit operator bool() const noexcept {
+ return static_cast<bool>(promise_);
+ }
+
+ private:
+ unique_ptr<PromiseInterface<T>> promise_;
+};
+
+template <class T = Unit>
+class SafePromise {
+ public:
+ SafePromise(Promise<T> promise, Result<T> result) : promise_(std::move(promise)), result_(std::move(result)) {
+ }
+ SafePromise(const SafePromise &other) = delete;
+ SafePromise &operator=(const SafePromise &other) = delete;
+ SafePromise(SafePromise &&other) = default;
+ SafePromise &operator=(SafePromise &&other) = default;
+ ~SafePromise() {
+ if (promise_) {
+ promise_.set_result(std::move(result_));
+ }
+ }
+ Promise<T> release() {
+ return std::move(promise_);
+ }
+
+ private:
+ Promise<T> promise_;
+ Result<T> result_;
+};
+
+template <class T>
+Promise<T>::Promise(SafePromise<T> &&other) : Promise(other.release()) {
+}
+
+template <class T>
+Promise<T> &Promise<T>::operator=(SafePromise<T> &&other) {
+ *this = other.release();
+ return *this;
+}
+
+namespace detail {
+template <class PromiseT>
+class CancellablePromise final : public PromiseT {
+ public:
+ template <class... ArgsT>
+ CancellablePromise(CancellationToken cancellation_token, ArgsT &&...args)
+ : PromiseT(std::forward<ArgsT>(args)...), cancellation_token_(std::move(cancellation_token)) {
+ }
+ bool is_cancellable() const final {
+ return true;
+ }
+ bool is_canceled() const final {
+ return static_cast<bool>(cancellation_token_);
+ }
+
+ private:
+ CancellationToken cancellation_token_;
+};
+
+template <class... ArgsT>
+class JoinPromise final : public PromiseInterface<Unit> {
+ public:
+ explicit JoinPromise(ArgsT &&...arg) : promises_(std::forward<ArgsT>(arg)...) {
+ }
+ void set_value(Unit &&) final {
+ tuple_for_each(promises_, [](auto &promise) { promise.set_value(Unit()); });
+ }
+ void set_error(Status &&error) final {
+ tuple_for_each(promises_, [&error](auto &promise) { promise.set_error(error.clone()); });
+ }
+
+ private:
+ std::tuple<std::decay_t<ArgsT>...> promises_;
+};
+} // namespace detail
+
+class PromiseCreator {
+ public:
+ template <class OkT, class ArgT = detail::drop_result_t<detail::get_arg_t<OkT>>>
+ static Promise<ArgT> lambda(OkT &&ok) {
+ return Promise<ArgT>(td::make_unique<detail::LambdaPromise<ArgT, std::decay_t<OkT>>>(std::forward<OkT>(ok)));
+ }
+
+ template <class OkT, class ArgT = detail::drop_result_t<detail::get_arg_t<OkT>>>
+ static auto cancellable_lambda(CancellationToken cancellation_token, OkT &&ok) {
+ return Promise<ArgT>(td::make_unique<detail::CancellablePromise<detail::LambdaPromise<ArgT, std::decay_t<OkT>>>>(
+ std::move(cancellation_token), std::forward<OkT>(ok)));
+ }
+
+ template <class... ArgsT>
+ static Promise<> join(ArgsT &&...args) {
+ return Promise<>(td::make_unique<detail::JoinPromise<ArgsT...>>(std::forward<ArgsT>(args)...));
+ }
+};
+
+inline void set_promises(vector<Promise<Unit>> &promises) {
+ auto moved_promises = std::move(promises);
+ promises.clear();
+
+ for (auto &promise : moved_promises) {
+ promise.set_value(Unit());
+ }
+}
+
+template <class T>
+void fail_promises(vector<Promise<T>> &promises, Status &&error) {
+ CHECK(error.is_error());
+ auto moved_promises = std::move(promises);
+ promises.clear();
+
+ auto size = moved_promises.size();
+ if (size == 0) {
+ return;
+ }
+ size--;
+ for (size_t i = 0; i < size; i++) {
+ auto &promise = moved_promises[i];
+ if (promise) {
+ promise.set_error(error.clone());
+ }
+ }
+ moved_promises[size].set_error(std::move(error));
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.cpp
index db11df4dfa..5b4c78d675 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -13,6 +13,7 @@
#include <openssl/rand.h>
#endif
+#include <atomic>
#include <cstring>
#include <limits>
#include <random>
@@ -20,19 +21,35 @@
namespace td {
#if TD_HAVE_OPENSSL
+
+namespace {
+std::atomic<int64> random_seed_generation{0};
+} // namespace
+
void Random::secure_bytes(MutableSlice dest) {
Random::secure_bytes(dest.ubegin(), dest.size());
}
void Random::secure_bytes(unsigned char *ptr, size_t size) {
- constexpr size_t buf_size = 512;
+ constexpr size_t BUF_SIZE = 512;
static TD_THREAD_LOCAL unsigned char *buf; // static zero-initialized
static TD_THREAD_LOCAL size_t buf_pos;
- if (init_thread_local<unsigned char[]>(buf, buf_size)) {
- buf_pos = buf_size;
+ static TD_THREAD_LOCAL int64 generation;
+ if (init_thread_local<unsigned char[]>(buf, BUF_SIZE)) {
+ buf_pos = BUF_SIZE;
+ generation = 0;
+ }
+ if (ptr == nullptr) {
+ MutableSlice(buf, BUF_SIZE).fill_zero_secure();
+ buf_pos = BUF_SIZE;
+ return;
+ }
+ if (generation != random_seed_generation.load(std::memory_order_relaxed)) {
+ generation = random_seed_generation.load(std::memory_order_acquire);
+ buf_pos = BUF_SIZE;
}
- auto ready = min(size, buf_size - buf_pos);
+ auto ready = min(size, BUF_SIZE - buf_pos);
if (ready != 0) {
std::memcpy(ptr, buf + buf_pos, ready);
buf_pos += ready;
@@ -42,8 +59,8 @@ void Random::secure_bytes(unsigned char *ptr, size_t size) {
return;
}
}
- if (size < buf_size) {
- int err = RAND_bytes(buf, static_cast<int>(buf_size));
+ if (size < BUF_SIZE) {
+ int err = RAND_bytes(buf, static_cast<int>(BUF_SIZE));
// TODO: it CAN fail
LOG_IF(FATAL, err != 1);
buf_pos = size;
@@ -68,6 +85,27 @@ int64 Random::secure_int64() {
secure_bytes(reinterpret_cast<unsigned char *>(&res), sizeof(int64));
return res;
}
+
+uint32 Random::secure_uint32() {
+ uint32 res = 0;
+ secure_bytes(reinterpret_cast<unsigned char *>(&res), sizeof(uint32));
+ return res;
+}
+
+uint64 Random::secure_uint64() {
+ uint64 res = 0;
+ secure_bytes(reinterpret_cast<unsigned char *>(&res), sizeof(uint64));
+ return res;
+}
+
+void Random::add_seed(Slice bytes, double entropy) {
+ RAND_add(bytes.data(), static_cast<int>(bytes.size()), entropy);
+ random_seed_generation++;
+}
+
+void Random::secure_cleanup() {
+ Random::secure_bytes(nullptr, 0);
+}
#endif
static unsigned int rand_device_helper() {
@@ -96,13 +134,70 @@ uint64 Random::fast_uint64() {
return static_cast<uint64>((*gen)());
}
-int Random::fast(int min, int max) {
- if (min == std::numeric_limits<int>::min() && max == std::numeric_limits<int>::max()) {
+int Random::fast(int min_value, int max_value) {
+ if (min_value == std::numeric_limits<int>::min() && max_value == std::numeric_limits<int>::max()) {
// to prevent integer overflow and division by zero
- min++;
+ min_value++;
+ }
+ DCHECK(min_value <= max_value);
+ return static_cast<int>(min_value + fast_uint32() % (max_value - min_value + 1)); // TODO signed_cast
+}
+
+double Random::fast(double min_value, double max_value) {
+ DCHECK(min_value <= max_value);
+ return min_value + fast_uint32() * 1.0 / std::numeric_limits<uint32>::max() * (max_value - min_value);
+}
+
+bool Random::fast_bool() {
+ return (fast_uint32() & 1) != 0;
+}
+
+Random::Xorshift128plus::Xorshift128plus(uint64 seed) {
+ auto next = [&] {
+ // splitmix64
+ seed += static_cast<uint64>(0x9E3779B97F4A7C15ull);
+ uint64 z = seed;
+ z = (z ^ (z >> 30)) * static_cast<uint64>(0xBF58476D1CE4E5B9ull);
+ z = (z ^ (z >> 27)) * static_cast<uint64>(0x94D049BB133111EBull);
+ return z ^ (z >> 31);
+ };
+ seed_[0] = next();
+ seed_[1] = next();
+}
+
+Random::Xorshift128plus::Xorshift128plus(uint64 seed_a, uint64 seed_b) {
+ seed_[0] = seed_a;
+ seed_[1] = seed_b;
+}
+
+uint64 Random::Xorshift128plus::operator()() {
+ uint64 x = seed_[0];
+ const uint64 y = seed_[1];
+ seed_[0] = y;
+ x ^= x << 23;
+ seed_[1] = x ^ y ^ (x >> 17) ^ (y >> 26);
+ return seed_[1] + y;
+}
+
+int Random::Xorshift128plus::fast(int min_value, int max_value) {
+ return static_cast<int>((*this)() % (max_value - min_value + 1) + min_value);
+}
+int64 Random::Xorshift128plus::fast64(int64 min_value, int64 max_value) {
+ return static_cast<int64>((*this)() % (max_value - min_value + 1) + min_value);
+}
+
+void Random::Xorshift128plus::bytes(MutableSlice dest) {
+ int cnt = 0;
+ uint64 buf = 0;
+ for (auto &c : dest) {
+ if (cnt == 0) {
+ buf = operator()();
+ cnt = 8;
+ }
+ cnt--;
+ c = static_cast<char>(buf & 255);
+ buf >>= 8;
}
- CHECK(min <= max);
- return static_cast<int>(min + fast_uint32() % (max - min + 1)); // TODO signed_cast
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.h
index efe5d64618..32cb71f770 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Random.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,6 +8,9 @@
#include "td/utils/common.h"
#include "td/utils/Slice.h"
+#include "td/utils/Span.h"
+
+#include <utility>
namespace td {
@@ -18,13 +21,49 @@ class Random {
static void secure_bytes(unsigned char *ptr, size_t size);
static int32 secure_int32();
static int64 secure_int64();
+ static uint32 secure_uint32();
+ static uint64 secure_uint64();
+
+ // works only for current thread
+ static void add_seed(Slice bytes, double entropy = 0);
+ static void secure_cleanup();
#endif
static uint32 fast_uint32();
static uint64 fast_uint64();
- // distribution is not uniform, min and max are included
- static int fast(int min, int max);
+ // distribution is not uniform, min_value and max_value are included
+ static int fast(int min_value, int max_value);
+ static double fast(double min_value, double max_value);
+ static bool fast_bool();
+
+ class Fast {
+ public:
+ uint64 operator()() {
+ return fast_uint64();
+ }
+ };
+ class Xorshift128plus {
+ public:
+ explicit Xorshift128plus(uint64 seed);
+ Xorshift128plus(uint64 seed_a, uint64 seed_b);
+ uint64 operator()();
+ int fast(int min_value, int max_value);
+ int64 fast64(int64 min_value, int64 max_value);
+ void bytes(MutableSlice dest);
+
+ private:
+ uint64 seed_[2];
+ };
};
+template <class T, class R>
+void random_shuffle(MutableSpan<T> v, R &rnd) {
+ for (size_t i = 1; i < v.size(); i++) {
+ auto pos = static_cast<size_t>(rnd()) % (i + 1);
+ using std::swap;
+ swap(v[i], v[pos]);
+ }
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ScopeGuard.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ScopeGuard.h
index a914ce357c..577514a44b 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/ScopeGuard.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ScopeGuard.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,6 +14,7 @@
#include <utility>
namespace td {
+
class Guard {
public:
Guard() = default;
@@ -28,7 +29,7 @@ class Guard {
};
template <class FunctionT>
-class LambdaGuard : public Guard {
+class LambdaGuard final : public Guard {
public:
explicit LambdaGuard(const FunctionT &func) : func_(func) {
}
@@ -41,11 +42,11 @@ class LambdaGuard : public Guard {
}
LambdaGuard &operator=(LambdaGuard &&other) = delete;
- void dismiss() {
+ void dismiss() final {
dismissed_ = true;
}
- ~LambdaGuard() {
+ ~LambdaGuard() final {
if (!dismissed_) {
func_();
}
@@ -57,8 +58,8 @@ class LambdaGuard : public Guard {
};
template <class F>
-std::unique_ptr<Guard> create_lambda_guard(F &&f) {
- return std::make_unique<LambdaGuard<F>>(std::forward<F>(f));
+unique_ptr<Guard> create_lambda_guard(F &&f) {
+ return make_unique<LambdaGuard<F>>(std::forward<F>(f));
}
template <class F>
std::shared_ptr<Guard> create_shared_lambda_guard(F &&f) {
@@ -73,4 +74,4 @@ auto operator+(ScopeExit, FunctionT &&func) {
} // namespace td
-#define SCOPE_EXIT auto TD_CONCAT(SCOPE_EXIT_VAR_, __LINE__) = ::td::ScopeExit() + [&]()
+#define SCOPE_EXIT auto TD_CONCAT(SCOPE_EXIT_VAR_, __LINE__) = ::td::ScopeExit() + [&]
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/SetNode.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/SetNode.h
new file mode 100644
index 0000000000..6e5960553d
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/SetNode.h
@@ -0,0 +1,129 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/HashTableUtils.h"
+
+#include <type_traits>
+#include <utility>
+
+namespace td {
+
+template <class KeyT, class Enable = void>
+struct SetNode {
+ using public_key_type = KeyT;
+ using public_type = const KeyT;
+ using second_type = KeyT; // TODO: remove second_type?
+
+ KeyT first;
+
+ const KeyT &key() const {
+ return first;
+ }
+
+ const KeyT &get_public() {
+ return first;
+ }
+
+ SetNode() : first() {
+ }
+ explicit SetNode(KeyT key) : first(std::move(key)) {
+ }
+ SetNode(const SetNode &other) = delete;
+ SetNode &operator=(const SetNode &other) = delete;
+ SetNode(SetNode &&other) noexcept {
+ *this = std::move(other);
+ }
+ void operator=(SetNode &&other) noexcept {
+ DCHECK(empty());
+ DCHECK(!other.empty());
+ first = std::move(other.first);
+ other.first = KeyT();
+ }
+ ~SetNode() = default;
+
+ void copy_from(const SetNode &other) {
+ DCHECK(empty());
+ first = other.first;
+ DCHECK(!empty());
+ }
+
+ bool empty() const {
+ return is_hash_table_key_empty(first);
+ }
+
+ void clear() {
+ first = KeyT();
+ DCHECK(empty());
+ }
+
+ void emplace(KeyT key) {
+ first = std::move(key);
+ }
+};
+
+template <class KeyT>
+struct SetNode<KeyT, typename std::enable_if_t<(sizeof(KeyT) > 28 * sizeof(void *))>> {
+ struct Impl {
+ using second_type = KeyT;
+
+ KeyT first;
+
+ template <class InputKeyT>
+ explicit Impl(InputKeyT &&key) : first(std::forward<InputKeyT>(key)) {
+ DCHECK(!is_hash_table_key_empty(first));
+ }
+ Impl(const Impl &other) = delete;
+ Impl &operator=(const Impl &other) = delete;
+ Impl(Impl &&other) = delete;
+ void operator=(Impl &&other) = delete;
+ };
+
+ using public_key_type = KeyT;
+ using public_type = const KeyT;
+ using second_type = KeyT; // TODO: remove second_type?
+
+ unique_ptr<Impl> impl_;
+
+ const KeyT &key() const {
+ DCHECK(!empty());
+ return impl_->first;
+ }
+
+ const KeyT &get_public() {
+ DCHECK(!empty());
+ return impl_->first;
+ }
+
+ SetNode() : impl_() {
+ }
+ explicit SetNode(KeyT key) : impl_(td::make_unique<Impl>(std::move(key))) {
+ }
+
+ void copy_from(const SetNode &other) {
+ DCHECK(empty());
+ impl_ = td::make_unique<Impl>(other.impl_->first);
+ DCHECK(!empty());
+ }
+
+ bool empty() const {
+ return impl_ == nullptr;
+ }
+
+ void clear() {
+ DCHECK(!empty());
+ impl_ = nullptr;
+ }
+
+ void emplace(KeyT key) {
+ DCHECK(empty());
+ impl_ = td::make_unique<Impl>(std::move(key));
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedObjectPool.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedObjectPool.h
index dc8512b268..6a7dcc8256 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedObjectPool.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedObjectPool.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,6 @@
#pragma once
#include "td/utils/common.h"
-
#include "td/utils/logging.h"
#include "td/utils/MpscLinkQueue.h"
@@ -34,7 +33,7 @@ class AtomicRefCnt {
}
private:
- std::atomic<uint64> cnt_;
+ std::atomic<uint64> cnt_{0};
};
template <class DataT, class DeleterT>
@@ -50,7 +49,7 @@ class SharedPtrRaw
CHECK(option_magic_ == Magic);
}
template <class... ArgsT>
- void init_data(ArgsT &&... args) {
+ void init_data(ArgsT &&...args) {
new (&option_data_) DataT(std::forward<ArgsT>(args)...);
}
void destroy_data() {
@@ -89,6 +88,7 @@ template <class T, class DeleterT = std::default_delete<T>>
class SharedPtr {
public:
using Raw = detail::SharedPtrRaw<T, DeleterT>;
+ struct acquire_t {};
SharedPtr() = default;
~SharedPtr() {
if (!raw_) {
@@ -97,27 +97,36 @@ class SharedPtr {
reset();
}
explicit SharedPtr(Raw *raw) : raw_(raw) {
- raw_->inc();
+ if (raw_) {
+ raw_->inc();
+ }
+ }
+ SharedPtr(acquire_t, Raw *raw) : raw_(raw) {
}
SharedPtr(const SharedPtr &other) : SharedPtr(other.raw_) {
}
SharedPtr &operator=(const SharedPtr &other) {
- other.raw_->inc();
+ if (this == &other) {
+ return *this;
+ }
+ if (other.raw_) {
+ other.raw_->inc();
+ }
reset(other.raw_);
return *this;
}
- SharedPtr(SharedPtr &&other) : raw_(other.raw_) {
+ SharedPtr(SharedPtr &&other) noexcept : raw_(other.raw_) {
other.raw_ = nullptr;
}
- SharedPtr &operator=(SharedPtr &&other) {
+ SharedPtr &operator=(SharedPtr &&other) noexcept {
reset(other.raw_);
other.raw_ = nullptr;
return *this;
}
- bool empty() const {
+ bool empty() const noexcept {
return raw_ == nullptr;
}
- explicit operator bool() const {
+ explicit operator bool() const noexcept {
return !empty();
}
uint64 use_cnt() const {
@@ -149,17 +158,20 @@ class SharedPtr {
}
template <class... ArgsT>
- static SharedPtr<T, DeleterT> create(ArgsT &&... args) {
- auto raw = std::make_unique<Raw>(DeleterT());
+ static SharedPtr<T, DeleterT> create(ArgsT &&...args) {
+ auto raw = make_unique<Raw>(DeleterT());
raw->init_data(std::forward<ArgsT>(args)...);
return SharedPtr<T, DeleterT>(raw.release());
}
template <class D, class... ArgsT>
- static SharedPtr<T, DeleterT> create_with_deleter(D &&d, ArgsT &&... args) {
- auto raw = std::make_unique<Raw>(std::forward<D>(d));
+ static SharedPtr<T, DeleterT> create_with_deleter(D &&d, ArgsT &&...args) {
+ auto raw = make_unique<Raw>(std::forward<D>(d));
raw->init_data(std::forward<ArgsT>(args)...);
return SharedPtr<T, DeleterT>(raw.release());
}
+ bool operator==(const SharedPtr<T, DeleterT> &other) const {
+ return raw_ == other.raw_;
+ }
private:
Raw *raw_{nullptr};
@@ -185,11 +197,11 @@ class SharedObjectPool {
while (free_queue_reader_.read()) {
free_cnt++;
}
- CHECK(free_cnt == allocated_.size()) << free_cnt << " " << allocated_.size();
+ LOG_CHECK(free_cnt == allocated_.size()) << free_cnt << " " << allocated_.size();
}
template <class... ArgsT>
- Ptr alloc(ArgsT &&... args) {
+ Ptr alloc(ArgsT &&...args) {
auto *raw = alloc_raw();
raw->init_data(std::forward<ArgsT>(args)...);
return Ptr(raw);
@@ -202,7 +214,7 @@ class SharedObjectPool {
return free_queue_reader_.calc_size();
}
- //non thread safe
+ // non-thread-safe
template <class F>
void for_each(F &&f) {
for (auto &raw : allocated_) {
@@ -220,7 +232,7 @@ class SharedObjectPool {
if (raw) {
return raw;
}
- allocated_.push_back(std::make_unique<Raw>(deleter()));
+ allocated_.push_back(make_unique<Raw>(deleter()));
return allocated_.back().get();
}
@@ -243,7 +255,7 @@ class SharedObjectPool {
Raw *get() const {
return raw_;
}
- explicit operator bool() const {
+ explicit operator bool() const noexcept {
return raw_ != nullptr;
}
@@ -268,7 +280,7 @@ class SharedObjectPool {
return Deleter(this);
}
- std::vector<std::unique_ptr<Raw>> allocated_;
+ std::vector<unique_ptr<Raw>> allocated_;
MpscLinkQueue<Node> free_queue_;
typename MpscLinkQueue<Node>::Reader free_queue_reader_;
};
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/GitInfo.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedSlice.cpp
index 976286b923..df157f4e6d 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/GitInfo.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedSlice.cpp
@@ -1,20 +1,17 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
-#include "td/utils/GitInfo.h"
+#include "td/utils/SharedSlice.h"
-#include "auto/git_info.h"
+#include "td/utils/buffer.h"
namespace td {
-CSlice GitInfo::commit() {
- return GIT_COMMIT;
-}
-bool GitInfo::is_dirty() {
- return GIT_DIRTY;
+BufferSlice SharedSlice::clone_as_buffer_slice() const {
+ return BufferSlice{as_slice()};
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedSlice.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedSlice.h
new file mode 100644
index 0000000000..e0ea0c17c0
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/SharedSlice.h
@@ -0,0 +1,382 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Slice.h"
+
+#include <atomic>
+#include <memory>
+#include <new>
+#include <type_traits>
+
+namespace td {
+
+namespace detail {
+struct SharedSliceHeader {
+ explicit SharedSliceHeader(size_t size) : size_{size} {
+ }
+
+ void inc() {
+ refcnt_.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ bool dec() {
+ return refcnt_.fetch_sub(1, std::memory_order_acq_rel) == 1;
+ }
+
+ bool is_unique() const {
+ // NB: race if std::memory_order_relaxed is used
+ // reader may see a change by a new writer
+ return refcnt_.load(std::memory_order_acquire) == 1;
+ }
+
+ size_t size() const {
+ return size_;
+ }
+
+ private:
+ std::atomic<uint64> refcnt_{1};
+ size_t size_;
+};
+
+struct UniqueSliceHeader {
+ explicit UniqueSliceHeader(size_t size) : size_{size} {
+ }
+
+ void inc() {
+ }
+
+ bool dec() {
+ return true;
+ }
+
+ bool is_unique() const {
+ return true;
+ }
+
+ size_t size() const {
+ return size_;
+ }
+
+ private:
+ size_t size_;
+};
+
+template <class HeaderT, bool zero_on_destruct = false>
+class UnsafeSharedSlice {
+ public:
+ UnsafeSharedSlice() = default;
+ UnsafeSharedSlice clone() const {
+ if (is_null()) {
+ return UnsafeSharedSlice();
+ }
+ header()->inc();
+ return UnsafeSharedSlice(ptr_.get());
+ }
+
+ bool is_null() const {
+ return !ptr_;
+ }
+
+ bool is_unique() const {
+ if (is_null()) {
+ return true;
+ }
+ return header()->is_unique();
+ }
+
+ MutableSlice as_mutable_slice() {
+ if (is_null()) {
+ return MutableSlice();
+ }
+ return MutableSlice(ptr_.get() + sizeof(HeaderT), header()->size());
+ }
+
+ Slice as_slice() const {
+ if (is_null()) {
+ return Slice();
+ }
+ return Slice(ptr_.get() + sizeof(HeaderT), header()->size());
+ }
+
+ size_t size() const {
+ if (is_null()) {
+ return 0;
+ }
+ return header()->size();
+ }
+
+ static UnsafeSharedSlice create(size_t size) {
+ static_assert(std::is_standard_layout<HeaderT>::value, "HeaderT must have statdard layout");
+ auto ptr = std::make_unique<char[]>(sizeof(HeaderT) + size);
+ auto header_ptr = new (ptr.get()) HeaderT(size);
+ CHECK(header_ptr == reinterpret_cast<HeaderT *>(ptr.get()));
+
+ return UnsafeSharedSlice(std::move(ptr));
+ }
+
+ static UnsafeSharedSlice create(Slice slice) {
+ auto res = create(slice.size());
+ res.as_mutable_slice().copy_from(slice);
+ return res;
+ }
+
+ void clear() {
+ ptr_.reset();
+ }
+
+ private:
+ explicit UnsafeSharedSlice(char *ptr) : ptr_(ptr) {
+ }
+ explicit UnsafeSharedSlice(std::unique_ptr<char[]> from) : ptr_(from.release()) {
+ }
+
+ HeaderT *header() const {
+ return reinterpret_cast<HeaderT *>(ptr_.get());
+ }
+
+ struct SharedSliceDestructor {
+ void operator()(char *ptr) {
+ auto header = reinterpret_cast<HeaderT *>(ptr);
+ if (header->dec()) {
+ if (zero_on_destruct) {
+ MutableSlice(ptr, sizeof(HeaderT) + header->size()).fill_zero_secure();
+ }
+ std::default_delete<char[]>()(ptr);
+ }
+ }
+ };
+
+ std::unique_ptr<char[], SharedSliceDestructor> ptr_;
+};
+} // namespace detail
+
+class BufferSlice;
+
+class UniqueSharedSlice;
+
+class SharedSlice {
+ using Impl = detail::UnsafeSharedSlice<detail::SharedSliceHeader>;
+
+ public:
+ SharedSlice() = default;
+
+ explicit SharedSlice(Slice slice) : impl_(Impl::create(slice)) {
+ }
+
+ explicit SharedSlice(UniqueSharedSlice from);
+
+ SharedSlice(const char *ptr, size_t size) : SharedSlice(Slice(ptr, size)) {
+ }
+
+ SharedSlice clone() const {
+ return SharedSlice(impl_.clone());
+ }
+
+ Slice as_slice() const {
+ return impl_.as_slice();
+ }
+
+ BufferSlice clone_as_buffer_slice() const;
+
+ operator Slice() const {
+ return as_slice();
+ }
+
+ // like in std::string
+ const char *data() const {
+ return as_slice().data();
+ }
+
+ char operator[](size_t at) const {
+ return as_slice()[at];
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+
+ size_t size() const {
+ return impl_.size();
+ }
+
+ // like in std::string
+ size_t length() const {
+ return size();
+ }
+
+ void clear() {
+ impl_.clear();
+ }
+
+ private:
+ friend class UniqueSharedSlice;
+ explicit SharedSlice(Impl impl) : impl_(std::move(impl)) {
+ }
+ Impl impl_;
+};
+
+class UniqueSharedSlice {
+ using Impl = detail::UnsafeSharedSlice<detail::SharedSliceHeader>;
+
+ public:
+ UniqueSharedSlice() = default;
+
+ explicit UniqueSharedSlice(size_t size) : impl_(Impl::create(size)) {
+ }
+ explicit UniqueSharedSlice(Slice slice) : impl_(Impl::create(slice)) {
+ }
+
+ UniqueSharedSlice(const char *ptr, size_t size) : UniqueSharedSlice(Slice(ptr, size)) {
+ }
+ explicit UniqueSharedSlice(SharedSlice from) : impl_() {
+ if (from.impl_.is_unique()) {
+ impl_ = std::move(from.impl_);
+ } else {
+ impl_ = Impl::create(from.as_slice());
+ }
+ }
+
+ UniqueSharedSlice copy() const {
+ return UniqueSharedSlice(as_slice());
+ }
+
+ Slice as_slice() const {
+ return impl_.as_slice();
+ }
+
+ MutableSlice as_mutable_slice() {
+ return impl_.as_mutable_slice();
+ }
+
+ operator Slice() const {
+ return as_slice();
+ }
+
+ // like in std::string
+ char *data() {
+ return as_mutable_slice().data();
+ }
+ const char *data() const {
+ return as_slice().data();
+ }
+ char operator[](size_t at) const {
+ return as_slice()[at];
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+
+ size_t size() const {
+ return impl_.size();
+ }
+
+ // like in std::string
+ size_t length() const {
+ return size();
+ }
+
+ void clear() {
+ impl_.clear();
+ }
+
+ private:
+ friend class SharedSlice;
+ explicit UniqueSharedSlice(Impl impl) : impl_(std::move(impl)) {
+ }
+ Impl impl_;
+};
+
+inline SharedSlice::SharedSlice(UniqueSharedSlice from) : impl_(std::move(from.impl_)) {
+}
+
+template <bool zero_on_destruct>
+class UniqueSliceImpl {
+ using Impl = detail::UnsafeSharedSlice<detail::UniqueSliceHeader, zero_on_destruct>;
+
+ public:
+ UniqueSliceImpl() = default;
+
+ explicit UniqueSliceImpl(size_t size) : impl_(Impl::create(size)) {
+ }
+ UniqueSliceImpl(size_t size, char c) : impl_(Impl::create(size)) {
+ as_mutable_slice().fill(c);
+ }
+ explicit UniqueSliceImpl(Slice slice) : impl_(Impl::create(slice)) {
+ }
+
+ UniqueSliceImpl(const char *ptr, size_t size) : UniqueSliceImpl(Slice(ptr, size)) {
+ }
+
+ UniqueSliceImpl copy() const {
+ return UniqueSliceImpl(as_slice());
+ }
+
+ Slice as_slice() const {
+ return impl_.as_slice();
+ }
+
+ MutableSlice as_mutable_slice() {
+ return impl_.as_mutable_slice();
+ }
+
+ operator Slice() const {
+ return as_slice();
+ }
+
+ // like in std::string
+ char *data() {
+ return as_mutable_slice().data();
+ }
+ const char *data() const {
+ return as_slice().data();
+ }
+ char operator[](size_t at) const {
+ return as_slice()[at];
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+
+ size_t size() const {
+ return impl_.size();
+ }
+
+ // like in std::string
+ size_t length() const {
+ return size();
+ }
+
+ void clear() {
+ impl_.clear();
+ }
+
+ private:
+ explicit UniqueSliceImpl(Impl impl) : impl_(std::move(impl)) {
+ }
+ Impl impl_;
+};
+
+using UniqueSlice = UniqueSliceImpl<false>;
+using SecureString = UniqueSliceImpl<true>;
+
+inline MutableSlice as_mutable_slice(UniqueSharedSlice &unique_shared_slice) {
+ return unique_shared_slice.as_mutable_slice();
+}
+
+inline MutableSlice as_mutable_slice(UniqueSlice &unique_slice) {
+ return unique_slice.as_mutable_slice();
+}
+
+inline MutableSlice as_mutable_slice(SecureString &secure_string) {
+ return secure_string.as_mutable_slice();
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice-decl.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice-decl.h
index 69b4a4ad21..12d0382861 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice-decl.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice-decl.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,10 +8,10 @@
#include "td/utils/common.h"
-#include <cstring>
#include <type_traits>
namespace td {
+
class Slice;
class MutableSlice {
@@ -26,9 +26,7 @@ class MutableSlice {
MutableSlice(unsigned char *s, size_t len);
MutableSlice(string &s);
template <class T>
- explicit MutableSlice(T s, std::enable_if_t<std::is_same<char *, T>::value, private_tag> = {})
- : MutableSlice(s, std::strlen(s)) {
- }
+ explicit MutableSlice(T s, std::enable_if_t<std::is_same<char *, T>::value, private_tag> = {});
MutableSlice(char *s, char *t);
MutableSlice(unsigned char *s, unsigned char *t);
template <size_t N>
@@ -54,11 +52,16 @@ class MutableSlice {
MutableSlice substr(size_t from, size_t size) const;
size_t find(char c) const;
size_t rfind(char c) const;
+ void fill(char c);
+ void fill_zero();
+ void fill_zero_secure();
void copy_from(Slice from);
char &back();
char &operator[](size_t i);
+
+ static const size_t npos = static_cast<size_t>(-1);
};
class Slice {
@@ -74,13 +77,9 @@ class Slice {
Slice(const unsigned char *s, size_t len);
Slice(const string &s);
template <class T>
- explicit Slice(T s, std::enable_if_t<std::is_same<char *, std::remove_const_t<T>>::value, private_tag> = {})
- : Slice(s, std::strlen(s)) {
- }
+ explicit Slice(T s, std::enable_if_t<std::is_same<char *, std::remove_const_t<T>>::value, private_tag> = {});
template <class T>
- explicit Slice(T s, std::enable_if_t<std::is_same<const char *, std::remove_const_t<T>>::value, private_tag> = {})
- : Slice(s, std::strlen(s)) {
- }
+ explicit Slice(T s, std::enable_if_t<std::is_same<const char *, std::remove_const_t<T>>::value, private_tag> = {});
Slice(const char *s, const char *t);
Slice(const unsigned char *s, const unsigned char *t);
@@ -91,6 +90,18 @@ class Slice {
constexpr Slice(const char (&a)[N]) : s_(a), len_(N - 1) {
}
+ Slice &operator=(string &&s) = delete;
+
+ template <size_t N>
+ constexpr Slice &operator=(char (&a)[N]) = delete;
+
+ template <size_t N>
+ constexpr Slice &operator=(const char (&a)[N]) {
+ s_ = a;
+ len_ = N - 1;
+ return *this;
+ }
+
bool empty() const;
size_t size() const;
@@ -114,10 +125,13 @@ class Slice {
char back() const;
char operator[](size_t i) const;
+
+ static const size_t npos = static_cast<size_t>(-1);
};
bool operator==(const Slice &a, const Slice &b);
bool operator!=(const Slice &a, const Slice &b);
+bool operator<(const Slice &a, const Slice &b);
class MutableCSlice : public MutableSlice {
struct private_tag {};
@@ -175,13 +189,24 @@ class CSlice : public Slice {
CSlice() : CSlice("") {
}
+ CSlice &operator=(string &&s) = delete;
+
+ template <size_t N>
+ constexpr CSlice &operator=(char (&a)[N]) = delete;
+
+ template <size_t N>
+ constexpr CSlice &operator=(const char (&a)[N]) {
+ this->Slice::operator=(a);
+ return *this;
+ }
+
const char *c_str() const {
return begin();
}
};
struct SliceHash {
- std::size_t operator()(Slice slice) const;
+ uint32 operator()(Slice slice) const;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.cpp
new file mode 100644
index 0000000000..921a824479
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.cpp
@@ -0,0 +1,34 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/Slice.h"
+
+#if TD_HAVE_OPENSSL
+#include <openssl/crypto.h>
+#endif
+
+namespace td {
+
+void MutableSlice::fill(char c) {
+ std::memset(data(), c, size());
+}
+
+void MutableSlice::fill_zero() {
+ fill('\0');
+}
+
+void MutableSlice::fill_zero_secure() {
+#if TD_HAVE_OPENSSL
+ OPENSSL_cleanse(begin(), size());
+#else
+ volatile char *ptr = begin();
+ for (size_t i = 0; i < size(); i++) {
+ ptr[i] = '\0';
+ }
+#endif
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.h
index a9bc6a7551..6c1efb0a69 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Slice.h
@@ -1,20 +1,20 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+#include "td/utils/common.h"
#include "td/utils/Slice-decl.h"
-#include "td/utils/logging.h"
-
#include <cstring>
+#include <type_traits>
namespace td {
-/*** MutableSlice ***/
-inline MutableSlice::MutableSlice() : MutableSlice(const_cast<char *>(""), static_cast<size_t>(0)) {
+
+inline MutableSlice::MutableSlice() : s_(const_cast<char *>("")), len_(0) {
}
inline MutableSlice::MutableSlice(char *s, size_t len) : s_(s), len_(len) {
@@ -25,7 +25,13 @@ inline MutableSlice::MutableSlice(unsigned char *s, size_t len) : s_(reinterpret
CHECK(s_ != nullptr);
}
-inline MutableSlice::MutableSlice(string &s) : MutableSlice(&s[0], s.size()) {
+inline MutableSlice::MutableSlice(string &s) : s_(&s[0]), len_(s.size()) {
+}
+
+template <class T>
+MutableSlice::MutableSlice(T s, std::enable_if_t<std::is_same<char *, T>::value, private_tag>) : s_(s) {
+ CHECK(s_ != nullptr);
+ len_ = std::strlen(s_);
}
inline MutableSlice::MutableSlice(char *s, char *t) : MutableSlice(s, t - s) {
@@ -104,7 +110,7 @@ inline size_t MutableSlice::find(char c) const {
return pos;
}
}
- return static_cast<size_t>(-1);
+ return npos;
}
inline size_t MutableSlice::rfind(char c) const {
@@ -113,7 +119,7 @@ inline size_t MutableSlice::rfind(char c) const {
return pos;
}
}
- return static_cast<size_t>(-1);
+ return npos;
}
inline void MutableSlice::copy_from(Slice from) {
@@ -130,11 +136,10 @@ inline char &MutableSlice::operator[](size_t i) {
return s_[i];
}
-/*** Slice ***/
-inline Slice::Slice() : Slice("", static_cast<size_t>(0)) {
+inline Slice::Slice() : s_(""), len_(0) {
}
-inline Slice::Slice(const MutableSlice &other) : Slice(other.begin(), other.size()) {
+inline Slice::Slice(const MutableSlice &other) : s_(other.begin()), len_(other.size()) {
}
inline Slice::Slice(const char *s, size_t len) : s_(s), len_(len) {
@@ -145,13 +150,28 @@ inline Slice::Slice(const unsigned char *s, size_t len) : s_(reinterpret_cast<co
CHECK(s_ != nullptr);
}
-inline Slice::Slice(const string &s) : Slice(s.c_str(), s.size()) {
+inline Slice::Slice(const string &s) : s_(s.c_str()), len_(s.size()) {
+}
+
+template <class T>
+Slice::Slice(T s, std::enable_if_t<std::is_same<char *, std::remove_const_t<T>>::value, private_tag>) : s_(s) {
+ CHECK(s_ != nullptr);
+ len_ = std::strlen(s_);
+}
+
+template <class T>
+Slice::Slice(T s, std::enable_if_t<std::is_same<const char *, std::remove_const_t<T>>::value, private_tag>) : s_(s) {
+ CHECK(s_ != nullptr);
+ len_ = std::strlen(s_);
}
-inline Slice::Slice(const char *s, const char *t) : Slice(s, t - s) {
+inline Slice::Slice(const char *s, const char *t) : s_(s), len_(t - s) {
+ CHECK(s_ != nullptr);
}
-inline Slice::Slice(const unsigned char *s, const unsigned char *t) : Slice(s, t - s) {
+inline Slice::Slice(const unsigned char *s, const unsigned char *t)
+ : s_(reinterpret_cast<const char *>(s)), len_(t - s) {
+ CHECK(s_ != nullptr);
}
inline size_t Slice::size() const {
@@ -225,7 +245,7 @@ inline size_t Slice::find(char c) const {
return pos;
}
}
- return static_cast<size_t>(-1);
+ return npos;
}
inline size_t Slice::rfind(char c) const {
@@ -234,7 +254,7 @@ inline size_t Slice::rfind(char c) const {
return pos;
}
}
- return static_cast<size_t>(-1);
+ return npos;
}
inline char Slice::back() const {
@@ -254,6 +274,14 @@ inline bool operator!=(const Slice &a, const Slice &b) {
return !(a == b);
}
+inline bool operator<(const Slice &a, const Slice &b) {
+ auto x = std::memcmp(a.data(), b.data(), td::min(a.size(), b.size()));
+ if (x == 0) {
+ return a.size() < b.size();
+ }
+ return x < 0;
+}
+
inline MutableCSlice::MutableCSlice(char *s, char *t) : MutableSlice(s, t) {
CHECK(*t == '\0');
}
@@ -262,14 +290,38 @@ inline CSlice::CSlice(const char *s, const char *t) : Slice(s, t) {
CHECK(*t == '\0');
}
-inline std::size_t SliceHash::operator()(Slice slice) const {
+inline uint32 SliceHash::operator()(Slice slice) const {
// simple string hash
- std::size_t result = 0;
- constexpr std::size_t MUL = 123456789;
+ uint32 result = 0;
+ constexpr uint32 MUL = 123456789;
for (auto c : slice) {
result = result * MUL + c;
}
return result;
}
+inline Slice as_slice(Slice slice) {
+ return slice;
+}
+
+inline MutableSlice as_slice(MutableSlice slice) {
+ return slice;
+}
+
+inline Slice as_slice(const string &str) {
+ return str;
+}
+
+inline MutableSlice as_slice(string &str) {
+ return str;
+}
+
+inline MutableSlice as_mutable_slice(MutableSlice slice) {
+ return slice;
+}
+
+inline MutableSlice as_mutable_slice(string &str) {
+ return str;
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/SliceBuilder.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/SliceBuilder.h
new file mode 100644
index 0000000000..062ea136a0
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/SliceBuilder.h
@@ -0,0 +1,57 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Slice.h"
+#include "td/utils/StackAllocator.h"
+#include "td/utils/StringBuilder.h"
+
+#define PSLICE() ::td::detail::Slicify() & ::td::SliceBuilder().ref()
+#define PSTRING() ::td::detail::Stringify() & ::td::SliceBuilder().ref()
+
+namespace td {
+
+class SliceBuilder {
+ public:
+ template <class T>
+ SliceBuilder &operator<<(T &&other) {
+ sb_ << other;
+ return *this;
+ }
+
+ MutableCSlice as_cslice() {
+ return sb_.as_cslice();
+ }
+
+ SliceBuilder &ref() {
+ return *this;
+ }
+
+ private:
+ static const size_t DEFAULT_BUFFER_SIZE = 1024;
+ decltype(StackAllocator::alloc(0)) buffer_ = StackAllocator::alloc(DEFAULT_BUFFER_SIZE);
+ StringBuilder sb_ = StringBuilder(buffer_.as_slice(), true);
+};
+
+namespace detail {
+class Slicify {
+ public:
+ CSlice operator&(SliceBuilder &slice_builder) {
+ return slice_builder.as_cslice();
+ }
+};
+
+class Stringify {
+ public:
+ string operator&(SliceBuilder &slice_builder) {
+ return slice_builder.as_cslice().str();
+ }
+};
+} // namespace detail
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Span.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Span.h
new file mode 100644
index 0000000000..877adcf78a
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Span.h
@@ -0,0 +1,158 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#include <array>
+#include <iterator>
+
+namespace td {
+
+namespace detail {
+template <class T, class InnerT>
+class SpanImpl {
+ InnerT *data_{nullptr};
+ size_t size_{0};
+
+ public:
+ SpanImpl() = default;
+ SpanImpl(InnerT *data, size_t size) : data_(data), size_(size) {
+ }
+ SpanImpl(InnerT &data) : SpanImpl(&data, 1) {
+ }
+
+ template <class OtherInnerT>
+ SpanImpl(const SpanImpl<T, OtherInnerT> &other) : SpanImpl(other.data(), other.size()) {
+ }
+
+ template <size_t N>
+ SpanImpl(const std::array<T, N> &arr) : SpanImpl(arr.data(), arr.size()) {
+ }
+ template <size_t N>
+ SpanImpl(std::array<T, N> &arr) : SpanImpl(arr.data(), arr.size()) {
+ }
+ template <size_t N>
+ SpanImpl(const T (&arr)[N]) : SpanImpl(arr, N) {
+ }
+ template <size_t N>
+ SpanImpl(T (&arr)[N]) : SpanImpl(arr, N) {
+ }
+ SpanImpl(const vector<T> &v) : SpanImpl(v.data(), v.size()) {
+ }
+ SpanImpl(vector<T> &v) : SpanImpl(v.data(), v.size()) {
+ }
+
+ template <class OtherInnerT>
+ SpanImpl &operator=(const SpanImpl<T, OtherInnerT> &other) {
+ SpanImpl copy{other};
+ *this = copy;
+ }
+ template <class OtherInnerT>
+ bool operator==(const SpanImpl<T, OtherInnerT> &other) const {
+ if (size() != other.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < size(); i++) {
+ if (!((*this)[i] == other[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ InnerT &operator[](size_t i) {
+ DCHECK(i < size());
+ return data_[i];
+ }
+
+ const InnerT &operator[](size_t i) const {
+ DCHECK(i < size());
+ return data_[i];
+ }
+
+ InnerT &back() {
+ DCHECK(!empty());
+ return data_[size() - 1];
+ }
+
+ const InnerT &back() const {
+ DCHECK(!empty());
+ return data_[size() - 1];
+ }
+
+ InnerT &front() {
+ DCHECK(!empty());
+ return data_[0];
+ }
+
+ const InnerT &front() const {
+ DCHECK(!empty());
+ return data_[0];
+ }
+
+ InnerT *data() const {
+ return data_;
+ }
+
+ InnerT *begin() const {
+ return data_;
+ }
+ InnerT *end() const {
+ return data_ + size_;
+ }
+
+ std::reverse_iterator<InnerT *> rbegin() const {
+ return std::reverse_iterator<InnerT *>(end());
+ }
+ std::reverse_iterator<InnerT *> rend() const {
+ return std::reverse_iterator<InnerT *>(begin());
+ }
+
+ size_t size() const {
+ return size_;
+ }
+ bool empty() const {
+ return size() == 0;
+ }
+
+ SpanImpl &truncate(size_t size) {
+ if (size < size_) {
+ size_ = size;
+ }
+ return *this;
+ }
+
+ SpanImpl substr(size_t offset) const {
+ CHECK(offset <= size_);
+ return SpanImpl(begin() + offset, size_ - offset);
+ }
+ SpanImpl substr(size_t offset, size_t size) const {
+ CHECK(offset <= size_);
+ CHECK(size_ - offset >= size);
+ return SpanImpl(begin() + offset, size);
+ }
+};
+} // namespace detail
+
+template <class T>
+using Span = detail::SpanImpl<T, const T>;
+
+template <class T>
+using MutableSpan = detail::SpanImpl<T, T>;
+
+template <class T>
+Span<T> as_span(const std::vector<T> &vec) {
+ return Span<T>(vec);
+}
+
+template <class T>
+MutableSpan<T> as_mutable_span(std::vector<T> &vec) {
+ return MutableSpan<T>(vec);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/SpinLock.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/SpinLock.h
index d726b0b2f6..3413f6c5b3 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/SpinLock.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/SpinLock.h
@@ -1,14 +1,15 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/port/thread.h"
+#include "td/utils/port/sleep.h"
#include <atomic>
+#include <memory>
namespace td {
@@ -26,9 +27,10 @@ class SpinLock {
bool next() {
cnt++;
if (cnt < 50) {
+ //TODO pause
return true;
} else {
- td::this_thread::yield();
+ usleep_for(1);
return true;
}
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.cpp
index 4db905368b..947238165e 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,11 +8,73 @@
#include "td/utils/port/thread_local.h"
+#include <array>
+#include <cstdlib>
+
namespace td {
+namespace {
+class ArrayAllocator final : public StackAllocator::AllocatorImpl {
+ static const size_t MEM_SIZE = 1024 * 1024;
+ std::array<char, MEM_SIZE> mem;
+ size_t pos{0};
+
+ MutableSlice allocate(size_t size) final {
+ if (size > MEM_SIZE) {
+ std::abort(); // too much memory requested
+ }
+ char *res = mem.data() + pos;
+ pos += (size + 7) & -8;
+ if (pos > MEM_SIZE) {
+ std::abort(); // memory is over
+ }
+ return {res, size};
+ }
+
+ void free_ptr(char *ptr, size_t size) final {
+ size = (size + 7) & -8;
+ if (size > pos || ptr != mem.data() + (pos - size)) {
+ std::abort(); // shouldn't happen
+ }
+ pos -= size;
+ }
+
+ public:
+ ~ArrayAllocator() final {
+ if (pos != 0) {
+ std::abort(); // shouldn't happen
+ }
+ }
+};
+
+class NewAllocator final : public StackAllocator::AllocatorImpl {
+ MutableSlice allocate(size_t size) final {
+ return {new char[size], size};
+ }
-StackAllocator::Impl &StackAllocator::impl() {
- static TD_THREAD_LOCAL StackAllocator::Impl *impl; // static zero-initialized
- init_thread_local<Impl>(impl);
- return *impl;
+ void free_ptr(char *ptr, size_t size) final {
+ delete[] ptr;
+ }
+
+ public:
+ ~NewAllocator() final = default;
+};
+} // namespace
+
+StackAllocator::Ptr::~Ptr() {
+ if (!slice_.empty()) {
+ allocator_->free_ptr(slice_.data(), slice_.size());
+ }
}
+
+StackAllocator::AllocatorImpl *StackAllocator::impl() {
+ if (get_thread_id() != 0) {
+ static TD_THREAD_LOCAL ArrayAllocator *array_allocator; // static zero-initialized
+ init_thread_local<ArrayAllocator>(array_allocator);
+ return array_allocator;
+ } else {
+ static NewAllocator new_allocator;
+ return &new_allocator;
+ }
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.h
index d2399b9526..7b87aef963 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/StackAllocator.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,75 +7,54 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/MovableValue.h"
-#include "td/utils/Slice-decl.h"
-
-#include <array>
-#include <cstdlib>
+#include "td/utils/Slice.h"
namespace td {
class StackAllocator {
- class Deleter {
+ public:
+ class AllocatorImpl {
public:
- void operator()(char *ptr) {
- free_ptr(ptr);
- }
+ AllocatorImpl() = default;
+ AllocatorImpl(const AllocatorImpl &) = delete;
+ AllocatorImpl &operator=(const AllocatorImpl &) = delete;
+ AllocatorImpl(AllocatorImpl &&) = delete;
+ AllocatorImpl &operator=(AllocatorImpl &&) = delete;
+ virtual ~AllocatorImpl() = default;
+
+ virtual MutableSlice allocate(size_t size) = 0;
+
+ virtual void free_ptr(char *ptr, size_t size) = 0;
};
- // TODO: alloc memory with mmap and unload unused pages
- // memory still can be corrupted, but it is better than explicit free function
- // TODO: use pointer that can't be even copied
- using PtrImpl = std::unique_ptr<char, Deleter>;
+ private:
class Ptr {
public:
- Ptr(char *ptr, size_t size) : ptr_(ptr), size_(size) {
+ Ptr(AllocatorImpl *allocator, size_t size) : allocator_(allocator), slice_(allocator_->allocate(size)) {
+ }
+ Ptr(const Ptr &other) = delete;
+ Ptr &operator=(const Ptr &other) = delete;
+ Ptr(Ptr &&other) noexcept : allocator_(other.allocator_), slice_(other.slice_) {
+ other.allocator_ = nullptr;
+ other.slice_ = MutableSlice();
}
+ Ptr &operator=(Ptr &&other) = delete;
+ ~Ptr();
MutableSlice as_slice() const {
- return MutableSlice(ptr_.get(), size_.get());
+ return slice_;
}
private:
- PtrImpl ptr_;
- MovableValue<size_t> size_;
- };
-
- static void free_ptr(char *ptr) {
- impl().free_ptr(ptr);
- }
-
- struct Impl {
- static const size_t MEM_SIZE = 1024 * 1024;
- std::array<char, MEM_SIZE> mem;
-
- size_t pos{0};
- char *alloc(size_t size) {
- if (size == 0) {
- size = 1;
- }
- char *res = mem.data() + pos;
- size = (size + 7) & -8;
- pos += size;
- if (pos > MEM_SIZE) {
- std::abort(); // memory is over
- }
- return res;
- }
- void free_ptr(char *ptr) {
- size_t new_pos = ptr - mem.data();
- if (new_pos >= pos) {
- std::abort(); // shouldn't happen
- }
- pos = new_pos;
- }
+ AllocatorImpl *allocator_;
+ MutableSlice slice_;
};
- static Impl &impl();
+ static AllocatorImpl *impl();
public:
static Ptr alloc(size_t size) {
- return Ptr(impl().alloc(size), size);
+ return Ptr(impl(), size);
}
};
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.cpp
index b8bb169e60..5a49b1097f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.cpp
@@ -1,11 +1,13 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/Status.h"
+#include "td/utils/SliceBuilder.h"
+
#if TD_PORT_WINDOWS
#include "td/utils/port/wstring_convert.h"
#endif
@@ -42,13 +44,45 @@ string winerror_to_string(int code) {
wchar_t wbuf[size];
auto res_size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, code, 0, wbuf, size - 1, nullptr);
if (res_size == 0) {
- return "Unknown windows error";
+ return "Unknown Windows error";
}
while (res_size != 0 && (wbuf[res_size - 1] == '\n' || wbuf[res_size - 1] == '\r')) {
res_size--;
}
- return from_wstring(wbuf, res_size).ok();
+ auto error_message = from_wstring(wbuf, res_size);
+ if (error_message.is_error()) {
+ return "Invalid Windows error";
+ }
+ return error_message.move_as_ok();
}
#endif
+Status Status::move_as_error_prefix(Slice prefix) const {
+ CHECK(is_error());
+ Info info = get_info();
+ switch (info.error_type) {
+ case ErrorType::General:
+ return Error(code(), PSLICE() << prefix << message());
+ case ErrorType::Os:
+ return Status(false, ErrorType::Os, code(), PSLICE() << prefix << message());
+ default:
+ UNREACHABLE();
+ return {};
+ }
+}
+
+Status Status::move_as_error_suffix(Slice suffix) const {
+ CHECK(is_error());
+ Info info = get_info();
+ switch (info.error_type) {
+ case ErrorType::General:
+ return Error(code(), PSLICE() << message() << suffix);
+ case ErrorType::Os:
+ return Status(false, ErrorType::Os, code(), PSLICE() << message() << suffix);
+ default:
+ UNREACHABLE();
+ return {};
+ }
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.h
index 8ef2846df1..83e1abcb23 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Status.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,7 +15,9 @@
#include <cerrno>
#include <cstring>
+#include <memory>
#include <new>
+#include <type_traits>
#include <utility>
#define TRY_STATUS(status) \
@@ -25,14 +27,84 @@
return try_status.move_as_error(); \
} \
}
-#define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
+
+#define TRY_STATUS_PREFIX(status, prefix) \
+ { \
+ auto try_status = (status); \
+ if (try_status.is_error()) { \
+ return try_status.move_as_error_prefix(prefix); \
+ } \
+ }
+
+#define TRY_STATUS_PROMISE(promise_name, status) \
+ { \
+ auto try_status = (status); \
+ if (try_status.is_error()) { \
+ promise_name.set_error(try_status.move_as_error()); \
+ return; \
+ } \
+ }
+
+#define TRY_STATUS_PROMISE_PREFIX(promise_name, status, prefix) \
+ { \
+ auto try_status = (status); \
+ if (try_status.is_error()) { \
+ promise_name.set_error(try_status.move_as_error_prefix(prefix)); \
+ return; \
+ } \
+ }
+
+#define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
+
+#define TRY_RESULT_PROMISE(promise_name, name, result) \
+ TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
+
+#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(r_response, __LINE__), name, result)
+
+#define TRY_RESULT_PROMISE_ASSIGN(promise_name, name, result) \
+ TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
+
+#define TRY_RESULT_PREFIX(name, result, prefix) \
+ TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
+
+#define TRY_RESULT_PREFIX_ASSIGN(name, result, prefix) \
+ TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
+
+#define TRY_RESULT_PROMISE_PREFIX(promise_name, name, result, prefix) \
+ TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
+
+#define TRY_RESULT_PROMISE_PREFIX_ASSIGN(promise_name, name, result, prefix) \
+ TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
#define TRY_RESULT_IMPL(r_name, name, result) \
auto r_name = (result); \
if (r_name.is_error()) { \
return r_name.move_as_error(); \
} \
- auto name = r_name.move_as_ok();
+ name = r_name.move_as_ok();
+
+#define TRY_RESULT_PREFIX_IMPL(r_name, name, result, prefix) \
+ auto r_name = (result); \
+ if (r_name.is_error()) { \
+ return r_name.move_as_error_prefix(prefix); \
+ } \
+ name = r_name.move_as_ok();
+
+#define TRY_RESULT_PROMISE_IMPL(promise_name, r_name, name, result) \
+ auto r_name = (result); \
+ if (r_name.is_error()) { \
+ promise_name.set_error(r_name.move_as_error()); \
+ return; \
+ } \
+ name = r_name.move_as_ok();
+
+#define TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, r_name, name, result, prefix) \
+ auto r_name = (result); \
+ if (r_name.is_error()) { \
+ promise_name.set_error(r_name.move_as_error_prefix(prefix)); \
+ return; \
+ } \
+ name = r_name.move_as_ok();
#define LOG_STATUS(status) \
{ \
@@ -49,19 +121,19 @@
#if TD_PORT_POSIX
#define OS_ERROR(message) \
- [&]() { \
+ [&] { \
auto saved_errno = errno; \
return ::td::Status::PosixError(saved_errno, (message)); \
}()
#define OS_SOCKET_ERROR(message) OS_ERROR(message)
#elif TD_PORT_WINDOWS
#define OS_ERROR(message) \
- [&]() { \
+ [&] { \
auto saved_error = ::GetLastError(); \
return ::td::Status::WindowsError(saved_error, (message)); \
}()
#define OS_SOCKET_ERROR(message) \
- [&]() { \
+ [&] { \
auto saved_error = ::WSAGetLastError(); \
return ::td::Status::WindowsError(saved_error, (message)); \
}()
@@ -78,16 +150,13 @@ string winerror_to_string(int code);
#endif
class Status {
- enum class ErrorType : int8 { general, os };
+ enum class ErrorType : int8 { General, Os };
public:
Status() = default;
bool operator==(const Status &other) const {
- if (get_info().static_flag) {
- return ptr_ == other.ptr_;
- }
- return false;
+ return ptr_ == other.ptr_;
}
Status clone() const TD_WARN_UNUSED_RESULT {
@@ -106,7 +175,7 @@ class Status {
}
static Status Error(int err, Slice message = Slice()) TD_WARN_UNUSED_RESULT {
- return Status(false, ErrorType::general, err, message);
+ return Status(false, ErrorType::General, err, message);
}
static Status Error(Slice message) TD_WARN_UNUSED_RESULT {
@@ -115,13 +184,13 @@ class Status {
#if TD_PORT_WINDOWS
static Status WindowsError(int saved_error, Slice message) TD_WARN_UNUSED_RESULT {
- return Status(false, ErrorType::os, saved_error, message);
+ return Status(false, ErrorType::Os, saved_error, message);
}
#endif
#if TD_PORT_POSIX
static Status PosixError(int32 saved_errno, Slice message) TD_WARN_UNUSED_RESULT {
- return Status(false, ErrorType::os, saved_errno, message);
+ return Status(false, ErrorType::Os, saved_errno, message);
}
#endif
@@ -131,17 +200,7 @@ class Status {
template <int Code>
static Status Error() {
- static Status status(true, ErrorType::general, Code, "");
- return status.clone_static();
- }
-
- static Status InvalidId() TD_WARN_UNUSED_RESULT {
- static Status status(true, ErrorType::general, 0, "Invalid Id");
- return status.clone_static();
- }
-
- static Status Hangup() TD_WARN_UNUSED_RESULT {
- static Status status(true, ErrorType::general, 0, "Hangup");
+ static Status status(true, ErrorType::General, Code, "");
return status.clone_static();
}
@@ -151,10 +210,10 @@ class Status {
}
Info info = get_info();
switch (info.error_type) {
- case ErrorType::general:
+ case ErrorType::General:
sb << "[Error";
break;
- case ErrorType::os:
+ case ErrorType::Os:
#if TD_PORT_POSIX
sb << "[PosixError : " << strerror_safe(info.error_code);
#elif TD_PORT_WINDOWS
@@ -162,7 +221,6 @@ class Status {
#endif
break;
default:
- LOG(FATAL) << "Unknown status type: " << static_cast<int8>(info.error_type);
UNREACHABLE();
break;
}
@@ -186,9 +244,21 @@ class Status {
return ptr_ != nullptr;
}
+#ifdef TD_STATUS_NO_ENSURE
+ void ensure() const {
+ if (!is_ok()) {
+ LOG(FATAL) << "Unexpected Status " << to_string();
+ }
+ }
+ void ensure_error() const {
+ if (is_ok()) {
+ LOG(FATAL) << "Unexpected Status::OK";
+ }
+ }
+#else
void ensure_impl(CSlice file_name, int line) const {
if (!is_ok()) {
- LOG(FATAL) << "Unexpexted Status " << to_string() << " in file " << file_name << " at line " << line;
+ LOG(FATAL) << "Unexpected Status " << to_string() << " in file " << file_name << " at line " << line;
}
}
void ensure_error_impl(CSlice file_name, int line) const {
@@ -196,6 +266,7 @@ class Status {
LOG(FATAL) << "Unexpected Status::OK in file " << file_name << " at line " << line;
}
}
+#endif
void ignore() const {
// nop
@@ -221,16 +292,15 @@ class Status {
}
Info info = get_info();
switch (info.error_type) {
- case ErrorType::general:
+ case ErrorType::General:
return message().str();
- case ErrorType::os:
+ case ErrorType::Os:
#if TD_PORT_POSIX
return strerror_safe(info.error_code).str();
#elif TD_PORT_WINDOWS
return winerror_to_string(info.error_code);
#endif
default:
- LOG(FATAL) << "Unknown status type: " << static_cast<int8>(info.error_type);
UNREACHABLE();
return "";
}
@@ -248,6 +318,16 @@ class Status {
return std::move(*this);
}
+ Status move_as_ok() = delete;
+
+ Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT {
+ return status.move_as_error_suffix(message());
+ }
+
+ Status move_as_error_prefix(Slice prefix) const TD_WARN_UNUSED_RESULT;
+
+ Status move_as_error_suffix(Slice suffix) const TD_WARN_UNUSED_RESULT;
+
private:
struct Info {
bool static_flag : 1;
@@ -277,10 +357,13 @@ class Status {
Status(bool static_flag, ErrorType error_type, int error_code, Slice message)
: Status(to_info(static_flag, error_type, error_code), message) {
+ if (static_flag) {
+ TD_LSAN_IGNORE(ptr_.get());
+ }
}
Status clone_static() const TD_WARN_UNUSED_RESULT {
- CHECK(is_ok() || get_info().static_flag);
+ CHECK(ptr_ != nullptr && get_info().static_flag);
Status result;
result.ptr_ = std::unique_ptr<char[], Deleter>(ptr_.get());
return result;
@@ -325,24 +408,30 @@ class Status {
template <class T = Unit>
class Result {
public:
- Result() : status_(Status::Error()) {
+ using ValueT = T;
+ Result() : status_(Status::Error<-1>()) {
}
- template <class S>
+ template <class S, std::enable_if_t<!std::is_same<std::decay_t<S>, Result>::value, int> = 0>
Result(S &&x) : status_(), value_(std::forward<S>(x)) {
}
+ struct emplace_t {};
+ template <class... ArgsT>
+ Result(emplace_t, ArgsT &&...args) : status_(), value_(std::forward<ArgsT>(args)...) {
+ }
Result(Status &&status) : status_(std::move(status)) {
CHECK(status_.is_error());
}
Result(const Result &) = delete;
Result &operator=(const Result &) = delete;
- Result(Result &&other) : status_(std::move(other.status_)) {
+ Result(Result &&other) noexcept : status_(std::move(other.status_)) {
if (status_.is_ok()) {
new (&value_) T(std::move(other.value_));
other.value_.~T();
}
- other.status_ = Status::Error();
+ other.status_ = Status::Error<-2>();
}
- Result &operator=(Result &&other) {
+ Result &operator=(Result &&other) noexcept {
+ CHECK(this != &other);
if (status_.is_ok()) {
value_.~T();
}
@@ -358,21 +447,38 @@ class Result {
other.value_.~T();
}
status_ = std::move(other.status_);
- other.status_ = Status::Error();
+ other.status_ = Status::Error<-3>();
return *this;
}
+ template <class... ArgsT>
+ void emplace(ArgsT &&...args) {
+ if (status_.is_ok()) {
+ value_.~T();
+ }
+ new (&value_) T(std::forward<ArgsT>(args)...);
+ status_ = Status::OK();
+ }
~Result() {
if (status_.is_ok()) {
value_.~T();
}
}
+#ifdef TD_STATUS_NO_ENSURE
+ void ensure() const {
+ status_.ensure();
+ }
+ void ensure_error() const {
+ status_.ensure_error();
+ }
+#else
void ensure_impl(CSlice file_name, int line) const {
status_.ensure_impl(file_name, line);
}
void ensure_error_impl(CSlice file_name, int line) const {
status_.ensure_error_impl(file_name, line);
}
+#endif
void ignore() const {
status_.ignore();
}
@@ -389,20 +495,42 @@ class Result {
Status move_as_error() TD_WARN_UNUSED_RESULT {
CHECK(status_.is_error());
SCOPE_EXIT {
- status_ = Status::Error();
+ status_ = Status::Error<-4>();
};
return std::move(status_);
}
+ Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT {
+ SCOPE_EXIT {
+ status_ = Status::Error<-5>();
+ };
+ return status_.move_as_error_prefix(prefix);
+ }
+ Status move_as_error_prefix(const Status &prefix) TD_WARN_UNUSED_RESULT {
+ SCOPE_EXIT {
+ status_ = Status::Error<-6>();
+ };
+ return status_.move_as_error_prefix(prefix);
+ }
+ Status move_as_error_suffix(Slice suffix) TD_WARN_UNUSED_RESULT {
+ SCOPE_EXIT {
+ status_ = Status::Error<-7>();
+ };
+ return status_.move_as_error_suffix(suffix);
+ }
const T &ok() const {
- CHECK(status_.is_ok()) << status_;
+ LOG_CHECK(status_.is_ok()) << status_;
return value_;
}
T &ok_ref() {
- CHECK(status_.is_ok()) << status_;
+ LOG_CHECK(status_.is_ok()) << status_;
+ return value_;
+ }
+ const T &ok_ref() const {
+ LOG_CHECK(status_.is_ok()) << status_;
return value_;
}
T move_as_ok() {
- CHECK(status_.is_ok()) << status_;
+ LOG_CHECK(status_.is_ok()) << status_;
return std::move(value_);
}
@@ -416,6 +544,22 @@ class Result {
*this = Result<T>();
}
+ template <class F>
+ Result<decltype(std::declval<F>()(std::declval<T>()))> move_map(F &&f) {
+ if (is_error()) {
+ return move_as_error();
+ }
+ return f(move_as_ok());
+ }
+
+ template <class F>
+ decltype(std::declval<F>()(std::declval<T>())) move_fmap(F &&f) {
+ if (is_error()) {
+ return move_as_error();
+ }
+ return f(move_as_ok());
+ }
+
private:
Status status_;
union {
@@ -432,27 +576,4 @@ inline StringBuilder &operator<<(StringBuilder &string_builder, const Status &st
return status.print(string_builder);
}
-namespace detail {
-
-class SlicifySafe {
- public:
- Result<CSlice> operator&(Logger &logger) {
- if (logger.is_error()) {
- return Status::Error("Buffer overflow");
- }
- return logger.as_cslice();
- }
-};
-
-class StringifySafe {
- public:
- Result<string> operator&(Logger &logger) {
- if (logger.is_error()) {
- return Status::Error("Buffer overflow");
- }
- return logger.as_cslice().str();
- }
-};
-
-} // namespace detail
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/StealingQueue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/StealingQueue.h
new file mode 100644
index 0000000000..d2fb773f3a
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/StealingQueue.h
@@ -0,0 +1,125 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/misc.h"
+
+#include <array>
+#include <atomic>
+
+namespace td {
+
+template <class T, size_t N = 256>
+class StealingQueue {
+ public:
+ static_assert(N > 0 && (N & (N - 1)) == 0, "");
+
+ // tries to put a value
+ // returns if succeeded
+ // only owner is allowed to to do this
+ template <class F>
+ void local_push(T value, F &&overflow_f) {
+ while (true) {
+ auto tail = tail_.load(std::memory_order_relaxed);
+ auto head = head_.load(); // TODO: memory order
+
+ if (static_cast<size_t>(tail - head) < N) {
+ buf_[tail & MASK].store(value, std::memory_order_relaxed);
+ tail_.store(tail + 1, std::memory_order_release);
+ return;
+ }
+
+ // queue is full
+ // TODO: batch insert into global queue?
+ auto n = N / 2 + 1;
+ auto new_head = head + n;
+ if (!head_.compare_exchange_strong(head, new_head)) {
+ continue;
+ }
+
+ for (size_t i = 0; i < n; i++) {
+ overflow_f(buf_[(i + head) & MASK].load(std::memory_order_relaxed));
+ }
+ overflow_f(value);
+
+ return;
+ }
+ }
+
+ // tries to pop a value
+ // returns if succeeded
+ // only owner is allowed to do this
+ bool local_pop(T &value) {
+ auto tail = tail_.load(std::memory_order_relaxed);
+ auto head = head_.load();
+
+ if (head == tail) {
+ return false;
+ }
+
+ value = buf_[head & MASK].load(std::memory_order_relaxed);
+ return head_.compare_exchange_strong(head, head + 1);
+ }
+
+ bool steal(T &value, StealingQueue<T, N> &other) {
+ while (true) {
+ auto tail = tail_.load(std::memory_order_relaxed);
+ auto head = head_.load(); // TODO: memory order
+
+ auto other_head = other.head_.load();
+ auto other_tail = other.tail_.load(std::memory_order_acquire);
+
+ if (other_tail < other_head) {
+ continue;
+ }
+ auto n = narrow_cast<size_t>(other_tail - other_head);
+ if (n > N) {
+ continue;
+ }
+ n -= n / 2;
+ n = td::min(n, static_cast<size_t>(head + N - tail));
+ if (n == 0) {
+ return false;
+ }
+
+ for (size_t i = 0; i < n; i++) {
+ buf_[(i + tail) & MASK].store(other.buf_[(i + other_head) & MASK].load(std::memory_order_relaxed),
+ std::memory_order_relaxed);
+ }
+
+ if (!other.head_.compare_exchange_strong(other_head, other_head + n)) {
+ continue;
+ }
+
+ n--;
+ value = buf_[(tail + n) & MASK].load(std::memory_order_relaxed);
+ tail_.store(tail + n, std::memory_order_release);
+ return true;
+ }
+ }
+
+ StealingQueue() {
+ for (auto &x : buf_) {
+// workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658
+#if TD_GCC && GCC_VERSION <= 40902
+ x = T();
+#else
+ std::atomic_init(&x, T());
+#endif
+ }
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+ }
+
+ private:
+ std::atomic<int64> head_{0};
+ std::atomic<int64> tail_{0};
+ static constexpr size_t MASK{N - 1};
+ std::array<std::atomic<T>, N> buf_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Storer.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Storer.h
index 91750dcd44..7743b81ff5 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Storer.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Storer.h
@@ -1,10 +1,11 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+
#include "td/utils/StorerBase.h"
#include "td/utils/common.h"
@@ -15,16 +16,17 @@
#include <limits>
namespace td {
-class SliceStorer : public Storer {
+
+class SliceStorer final : public Storer {
Slice slice;
public:
explicit SliceStorer(Slice slice) : slice(slice) {
}
- size_t size() const override {
+ size_t size() const final {
return slice.size();
}
- size_t store(uint8 *ptr) const override {
+ size_t store(uint8 *ptr) const final {
std::memcpy(ptr, slice.ubegin(), slice.size());
return slice.size();
}
@@ -34,7 +36,7 @@ inline SliceStorer create_storer(Slice slice) {
return SliceStorer(slice);
}
-class ConcatStorer : public Storer {
+class ConcatStorer final : public Storer {
const Storer &a_;
const Storer &b_;
@@ -42,11 +44,11 @@ class ConcatStorer : public Storer {
ConcatStorer(const Storer &a, const Storer &b) : a_(a), b_(b) {
}
- size_t size() const override {
+ size_t size() const final {
return a_.size() + b_.size();
}
- size_t store(uint8 *ptr) const override {
+ size_t store(uint8 *ptr) const final {
uint8 *ptr_save = ptr;
ptr += a_.store(ptr);
ptr += b_.store(ptr);
@@ -59,17 +61,17 @@ inline ConcatStorer create_storer(const Storer &a, const Storer &b) {
}
template <class T>
-class DefaultStorer : public Storer {
+class DefaultStorer final : public Storer {
public:
explicit DefaultStorer(const T &object) : object_(object) {
}
- size_t size() const override {
+ size_t size() const final {
if (size_ == std::numeric_limits<size_t>::max()) {
size_ = tl_calc_length(object_);
}
return size_;
}
- size_t store(uint8 *ptr) const override {
+ size_t store(uint8 *ptr) const final {
return tl_store_unsafe(object_, ptr);
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/StorerBase.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/StorerBase.h
index e6fea28e16..05e5edc714 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/StorerBase.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/StorerBase.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -19,7 +19,7 @@ class Storer {
Storer &operator=(Storer &&) = default;
virtual ~Storer() = default;
virtual size_t size() const = 0;
- virtual size_t store(uint8 *ptr) const = 0;
+ virtual size_t store(uint8 *ptr) const TD_WARN_UNUSED_RESULT = 0;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.cpp
index ce64bbc9a6..d82fdbea53 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,70 +8,181 @@
#include "td/utils/misc.h"
#include "td/utils/port/thread_local.h"
+#include "td/utils/Slice.h"
#include <cstdio>
+#include <cstring>
+#include <limits>
#include <locale>
+#include <memory>
#include <sstream>
+#include <utility>
namespace td {
-// TODO: optimize
+StringBuilder::StringBuilder(MutableSlice slice, bool use_buffer)
+ : begin_ptr_(slice.begin()), current_ptr_(begin_ptr_), use_buffer_(use_buffer) {
+ if (slice.size() <= RESERVED_SIZE) {
+ auto buffer_size = RESERVED_SIZE + 100;
+ buffer_ = std::make_unique<char[]>(buffer_size);
+ begin_ptr_ = buffer_.get();
+ current_ptr_ = begin_ptr_;
+ end_ptr_ = begin_ptr_ + buffer_size - RESERVED_SIZE;
+ } else {
+ end_ptr_ = slice.end() - RESERVED_SIZE;
+ }
+}
+
+StringBuilder &StringBuilder::operator<<(Slice slice) {
+ size_t size = slice.size();
+ if (unlikely(!reserve(size))) {
+ if (end_ptr_ < current_ptr_) {
+ return on_error();
+ }
+ auto available_size = static_cast<size_t>(end_ptr_ + RESERVED_SIZE - 1 - current_ptr_);
+ if (size > available_size) {
+ error_flag_ = true;
+ size = available_size;
+ }
+ }
+
+ std::memcpy(current_ptr_, slice.begin(), size);
+ current_ptr_ += size;
+ return *this;
+}
+
+template <class T>
+static char *print_uint(char *current_ptr, T x) {
+ if (x < 100) {
+ if (x < 10) {
+ *current_ptr++ = static_cast<char>('0' + x);
+ } else {
+ *current_ptr++ = static_cast<char>('0' + x / 10);
+ *current_ptr++ = static_cast<char>('0' + x % 10);
+ }
+ return current_ptr;
+ }
+
+ auto begin_ptr = current_ptr;
+ do {
+ *current_ptr++ = static_cast<char>('0' + x % 10);
+ x /= 10;
+ } while (x > 0);
+
+ auto end_ptr = current_ptr - 1;
+ while (begin_ptr < end_ptr) {
+ std::swap(*begin_ptr++, *end_ptr--);
+ }
+
+ return current_ptr;
+}
+
+template <class T>
+static char *print_int(char *current_ptr, T x) {
+ if (x < 0) {
+ if (x == std::numeric_limits<T>::min()) {
+ current_ptr = print_int(current_ptr, x + 1);
+ CHECK(current_ptr[-1] != '9');
+ current_ptr[-1]++;
+ return current_ptr;
+ }
+
+ *current_ptr++ = '-';
+ x = -x;
+ }
+
+ return print_uint(current_ptr, x);
+}
+
+bool StringBuilder::reserve_inner(size_t size) {
+ if (!use_buffer_) {
+ return false;
+ }
+
+ size_t old_data_size = current_ptr_ - begin_ptr_;
+ if (size >= std::numeric_limits<size_t>::max() - RESERVED_SIZE - old_data_size - 1) {
+ return false;
+ }
+ size_t need_data_size = old_data_size + size;
+ size_t old_buffer_size = end_ptr_ - begin_ptr_;
+ if (old_buffer_size >= (std::numeric_limits<size_t>::max() - RESERVED_SIZE) / 2 - 2) {
+ return false;
+ }
+ size_t new_buffer_size = (old_buffer_size + 1) * 2;
+ if (new_buffer_size < need_data_size) {
+ new_buffer_size = need_data_size;
+ }
+ if (new_buffer_size < 100) {
+ new_buffer_size = 100;
+ }
+ new_buffer_size += RESERVED_SIZE;
+ auto new_buffer = std::make_unique<char[]>(new_buffer_size);
+ std::memcpy(new_buffer.get(), begin_ptr_, old_data_size);
+ buffer_ = std::move(new_buffer);
+ begin_ptr_ = buffer_.get();
+ current_ptr_ = begin_ptr_ + old_data_size;
+ end_ptr_ = begin_ptr_ + new_buffer_size - RESERVED_SIZE;
+ CHECK(end_ptr_ > current_ptr_);
+ CHECK(static_cast<size_t>(end_ptr_ - current_ptr_) >= size);
+ return true;
+}
+
StringBuilder &StringBuilder::operator<<(int x) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
- current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%d", x);
+ current_ptr_ = print_int(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(unsigned int x) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
- current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%u", x);
+ current_ptr_ = print_uint(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long int x) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
- current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%ld", x);
+ current_ptr_ = print_int(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long unsigned int x) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
- current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%lu", x);
+ current_ptr_ = print_uint(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long long int x) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
- current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%lld", x);
+ current_ptr_ = print_int(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long long unsigned int x) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
- current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%llu", x);
+ current_ptr_ = print_uint(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(FixedDouble x) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve(std::numeric_limits<double>::max_exponent10 + x.precision + 4))) {
return on_error();
}
static TD_THREAD_LOCAL std::stringstream *ss;
if (init_thread_local<std::stringstream>(ss)) {
- ss->imbue(std::locale::classic());
+ auto previous_locale = ss->imbue(std::locale::classic());
ss->setf(std::ios_base::fixed, std::ios_base::floatfield);
} else {
ss->str(std::string());
@@ -80,8 +191,8 @@ StringBuilder &StringBuilder::operator<<(FixedDouble x) {
ss->precision(x.precision);
*ss << x.d;
- int len = narrow_cast<int>(static_cast<std::streamoff>(ss->tellp()));
- auto left = end_ptr_ + reserved_size - current_ptr_;
+ auto len = narrow_cast<int>(static_cast<std::streamoff>(ss->tellp()));
+ auto left = end_ptr_ + RESERVED_SIZE - current_ptr_;
if (unlikely(len >= left)) {
error_flag_ = true;
len = left ? narrow_cast<int>(left - 1) : 0;
@@ -92,10 +203,10 @@ StringBuilder &StringBuilder::operator<<(FixedDouble x) {
}
StringBuilder &StringBuilder::operator<<(const void *ptr) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
- current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%p", ptr);
+ current_ptr_ += std::snprintf(current_ptr_, RESERVED_SIZE, "%p", ptr);
return *this;
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.h
index a6345a9273..37adab338e 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/StringBuilder.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,22 +7,19 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/Slice-decl.h"
+#include "td/utils/Slice.h"
#include "td/utils/StackAllocator.h"
#include <cstdlib>
-#include <cstring>
+#include <memory>
#include <type_traits>
namespace td {
class StringBuilder {
public:
- explicit StringBuilder(MutableSlice slice)
- : begin_ptr_(slice.begin()), current_ptr_(begin_ptr_), end_ptr_(slice.end() - reserved_size) {
- if (slice.size() <= reserved_size) {
- std::abort(); // shouldn't happen
- }
+ explicit StringBuilder(MutableSlice slice, bool use_buffer = false);
+ StringBuilder() : StringBuilder({}, true) {
}
void clear() {
@@ -30,8 +27,13 @@ class StringBuilder {
error_flag_ = false;
}
+ void pop_back() {
+ CHECK(current_ptr_ > begin_ptr_);
+ current_ptr_--;
+ }
+
MutableCSlice as_cslice() {
- if (current_ptr_ >= end_ptr_ + reserved_size) {
+ if (current_ptr_ >= end_ptr_ + RESERVED_SIZE) {
std::abort(); // shouldn't happen
}
*current_ptr_ = 0;
@@ -42,31 +44,33 @@ class StringBuilder {
return error_flag_;
}
- StringBuilder &operator<<(const char *str) {
+ template <class T>
+ std::enable_if_t<std::is_same<char *, std::remove_const_t<T>>::value, StringBuilder> &operator<<(T str) {
+ return *this << Slice(str);
+ }
+ template <class T>
+ std::enable_if_t<std::is_same<const char *, std::remove_const_t<T>>::value, StringBuilder> &operator<<(T str) {
return *this << Slice(str);
}
- StringBuilder &operator<<(Slice slice) {
- if (unlikely(end_ptr_ < current_ptr_)) {
- return on_error();
- }
- auto size = static_cast<size_t>(end_ptr_ + reserved_size - 1 - current_ptr_);
- if (unlikely(slice.size() > size)) {
- error_flag_ = true;
- } else {
- size = slice.size();
- }
- std::memcpy(current_ptr_, slice.begin(), size);
- current_ptr_ += size;
- return *this;
+ template <size_t N>
+ StringBuilder &operator<<(char (&str)[N]) = delete;
+
+ template <size_t N>
+ StringBuilder &operator<<(const char (&str)[N]) {
+ return *this << Slice(str, N - 1);
}
+ StringBuilder &operator<<(const wchar_t *str) = delete;
+
+ StringBuilder &operator<<(Slice slice);
+
StringBuilder &operator<<(bool b) {
return *this << (b ? Slice("true") : Slice("false"));
}
StringBuilder &operator<<(char c) {
- if (unlikely(end_ptr_ < current_ptr_)) {
+ if (unlikely(!reserve())) {
return on_error();
}
*current_ptr_++ = c;
@@ -108,22 +112,33 @@ class StringBuilder {
StringBuilder &operator<<(const void *ptr);
- template <class T>
- StringBuilder &operator<<(const T *ptr) {
- return *this << static_cast<const void *>(ptr);
- }
-
private:
char *begin_ptr_;
char *current_ptr_;
char *end_ptr_;
bool error_flag_ = false;
- static constexpr size_t reserved_size = 30;
+ bool use_buffer_ = false;
+ std::unique_ptr<char[]> buffer_;
+ static constexpr size_t RESERVED_SIZE = 30;
StringBuilder &on_error() {
error_flag_ = true;
return *this;
}
+
+ bool reserve() {
+ if (end_ptr_ > current_ptr_) {
+ return true;
+ }
+ return reserve_inner(RESERVED_SIZE);
+ }
+ bool reserve(size_t size) {
+ if (end_ptr_ > current_ptr_ && static_cast<size_t>(end_ptr_ - current_ptr_) >= size) {
+ return true;
+ }
+ return reserve_inner(size);
+ }
+ bool reserve_inner(size_t size);
};
template <class T>
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadLocalStorage.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadLocalStorage.h
new file mode 100644
index 0000000000..da42c4ac0e
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadLocalStorage.h
@@ -0,0 +1,55 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/port/thread_local.h"
+
+#include <array>
+#include <atomic>
+
+namespace td {
+
+template <class T>
+class ThreadLocalStorage {
+ public:
+ T &get() {
+ return thread_local_node().value;
+ }
+
+ template <class F>
+ void for_each(F &&f) {
+ int32 n = max_thread_id_.load();
+ for (int32 i = 0; i < n; i++) {
+ f(nodes_[i].value);
+ }
+ }
+ template <class F>
+ void for_each(F &&f) const {
+ int32 n = max_thread_id_.load();
+ for (int32 i = 0; i < n; i++) {
+ f(nodes_[i].value);
+ }
+ }
+
+ private:
+ struct Node {
+ T value;
+ char padding[TD_CONCURRENCY_PAD];
+ };
+ static constexpr int32 MAX_THREAD_ID = 128;
+ std::atomic<int32> max_thread_id_{MAX_THREAD_ID};
+ std::array<Node, MAX_THREAD_ID> nodes_;
+
+ Node &thread_local_node() {
+ auto thread_id = get_thread_id();
+ CHECK(0 <= thread_id && static_cast<size_t>(thread_id) < nodes_.size());
+ return nodes_[thread_id];
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadSafeCounter.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadSafeCounter.h
new file mode 100644
index 0000000000..2d6f3f2950
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/ThreadSafeCounter.h
@@ -0,0 +1,132 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Slice.h"
+#include "td/utils/StringBuilder.h"
+#include "td/utils/ThreadLocalStorage.h"
+
+#include <array>
+#include <atomic>
+#include <mutex>
+
+namespace td {
+
+template <size_t N>
+class ThreadSafeMultiCounter {
+ public:
+ void add(size_t index, int64 diff) {
+ CHECK(index < N);
+ tls_.get()[index].fetch_add(diff, std::memory_order_relaxed);
+ }
+
+ int64 sum(size_t index) const {
+ CHECK(index < N);
+ int64 res = 0;
+ tls_.for_each([&res, &index](auto &value) { res += value[index].load(std::memory_order_relaxed); });
+ return res;
+ }
+ void clear() {
+ tls_.for_each([](auto &value) {
+ for (auto &x : value) {
+ x = 0;
+ }
+ });
+ }
+
+ private:
+ ThreadLocalStorage<std::array<std::atomic<int64>, N>> tls_;
+};
+
+class ThreadSafeCounter {
+ public:
+ void add(int64 diff) {
+ counter_.add(0, diff);
+ }
+
+ int64 sum() const {
+ return counter_.sum(0);
+ }
+
+ void clear() {
+ counter_.clear();
+ }
+
+ private:
+ ThreadSafeMultiCounter<1> counter_;
+};
+
+class NamedThreadSafeCounter {
+ static constexpr int N = 128;
+ using Counter = ThreadSafeMultiCounter<N>;
+
+ public:
+ class CounterRef {
+ public:
+ CounterRef() = default;
+ CounterRef(size_t index, Counter *counter) : index_(index), counter_(counter) {
+ }
+ void add(int64 diff) {
+ counter_->add(index_, diff);
+ }
+ int64 sum() const {
+ return counter_->sum(index_);
+ }
+
+ private:
+ size_t index_{0};
+ Counter *counter_{nullptr};
+ };
+
+ CounterRef get_counter(Slice name) {
+ std::unique_lock<std::mutex> guard(mutex_);
+ for (size_t i = 0; i < names_.size(); i++) {
+ if (names_[i] == name) {
+ return get_counter_ref(i);
+ }
+ }
+ CHECK(names_.size() < N);
+ names_.emplace_back(name.begin(), name.size());
+ return get_counter_ref(names_.size() - 1);
+ }
+
+ CounterRef get_counter_ref(size_t index) {
+ return CounterRef(index, &counter_);
+ }
+
+ static NamedThreadSafeCounter &get_default() {
+ static NamedThreadSafeCounter res;
+ return res;
+ }
+
+ template <class F>
+ void for_each(F &&f) const {
+ std::unique_lock<std::mutex> guard(mutex_);
+ for (size_t i = 0; i < names_.size(); i++) {
+ f(names_[i], counter_.sum(i));
+ }
+ }
+
+ void clear() {
+ std::unique_lock<std::mutex> guard(mutex_);
+ counter_.clear();
+ }
+
+ friend StringBuilder &operator<<(StringBuilder &sb, const NamedThreadSafeCounter &counter) {
+ counter.for_each([&sb](Slice name, int64 cnt) { sb << name << ": " << cnt << "\n"; });
+ return sb;
+ }
+
+ private:
+ mutable std::mutex mutex_;
+ std::vector<std::string> names_;
+
+ Counter counter_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.cpp
index 3e62002c18..93ed7d1acd 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.cpp
@@ -1,19 +1,49 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/Time.h"
+#include "td/utils/port/Clocks.h"
+
+#include <atomic>
#include <cmath>
namespace td {
-std::atomic<double> Time::now_;
-
bool operator==(Timestamp a, Timestamp b) {
return std::abs(a.at() - b.at()) < 1e-6;
}
+static std::atomic<double> time_diff;
+
+double Time::now() {
+ auto result = now_unadjusted() + time_diff.load(std::memory_order_relaxed);
+ while (result < 0) {
+ auto old_time_diff = time_diff.load();
+ time_diff.compare_exchange_strong(old_time_diff, old_time_diff - result);
+ result = now_unadjusted() + time_diff.load(std::memory_order_relaxed);
+ }
+ return result;
+}
+
+double Time::now_unadjusted() {
+ return Clocks::monotonic();
+}
+
+void Time::jump_in_future(double at) {
+ while (true) {
+ auto old_time_diff = time_diff.load();
+ auto diff = at - now();
+ if (diff < 0) {
+ return;
+ }
+ if (time_diff.compare_exchange_strong(old_time_diff, old_time_diff + diff)) {
+ return;
+ }
+ }
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.h
index acdb8b52ef..4149c93d9d 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Time.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,25 +7,30 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/port/Clocks.h"
-
-#include <atomic>
namespace td {
class Time {
public:
- static double now() {
- double now = Clocks::monotonic();
- now_.store(now, std::memory_order_relaxed);
- return now;
- }
+ static double now();
static double now_cached() {
- return now_.load(std::memory_order_relaxed);
+ // Temporary(?) use now in now_cached
+ // Problem:
+ // thread A: check that now() > timestamp and notifies thread B
+ // thread B: must see that now() > timestamp()
+ //
+ // now() and now_cached() must be monotonic
+ //
+ // if a=now[_cached]() happens before b=now[_cached] than
+ // a <= b
+ //
+ // As an alternative we may say that now_cached is a thread local copy of now
+ return now();
}
+ static double now_unadjusted();
- private:
- static std::atomic<double> now_;
+ // Used for testing. After jump_in_future(at) is called, now() >= at.
+ static void jump_in_future(double at);
};
inline void relax_timeout_at(double *timeout, double new_timeout) {
@@ -53,15 +58,18 @@ class Timestamp {
return Timestamp{timeout};
}
- static Timestamp in(double timeout) {
- return Timestamp{Time::now_cached() + timeout};
+ static Timestamp in(double timeout, Timestamp now = now_cached()) {
+ return Timestamp{now.at() + timeout};
}
+ bool is_in_past(Timestamp now) const {
+ return at_ <= now.at();
+ }
bool is_in_past() const {
- return at_ <= Time::now_cached();
+ return is_in_past(now_cached());
}
- explicit operator bool() const {
+ explicit operator bool() const noexcept {
return at_ > 0;
}
@@ -91,14 +99,8 @@ class Timestamp {
}
};
-template <class T>
-void parse(Timestamp &timestamp, T &parser) {
- timestamp = Timestamp::in(parser.fetch_double() - Clocks::system());
-}
-
-template <class T>
-void store(const Timestamp &timestamp, T &storer) {
- storer.store_binary(timestamp.at() - Time::now() + Clocks::system());
+inline bool operator<(const Timestamp &a, const Timestamp &b) {
+ return a.at() < b.at();
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TimedStat.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/TimedStat.h
index fc4197470d..db0e07495b 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/TimedStat.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TimedStat.h
@@ -1,13 +1,15 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/logging.h"
+#include "td/utils/common.h"
+#include "td/utils/optional.h"
+#include <functional>
#include <utility>
namespace td {
@@ -16,7 +18,7 @@ template <class StatT>
class TimedStat {
public:
TimedStat(double duration, double now)
- : duration_(duration), current_(), current_timestamp_(now), next_(), next_timestamp_(now) {
+ : duration_(duration), current_(), current_timestamp_(now - 1), next_(), next_timestamp_(now) {
}
TimedStat() : TimedStat(0, 0) {
}
@@ -48,7 +50,7 @@ class TimedStat {
void update(double &now) {
if (now < next_timestamp_) {
- CHECK(now >= next_timestamp_ * (1 - 1e-14)) << now << " " << next_timestamp_;
+ // LOG_CHECK(now >= next_timestamp_ * (1 - 1e-14)) << now << " " << next_timestamp_;
now = next_timestamp_;
}
if (duration_ == 0) {
@@ -56,7 +58,7 @@ class TimedStat {
}
if (next_timestamp_ + 2 * duration_ < now) {
current_ = StatT();
- current_timestamp_ = now;
+ current_timestamp_ = now - duration_;
next_ = StatT();
next_timestamp_ = now;
} else if (next_timestamp_ + duration_ < now) {
@@ -68,4 +70,28 @@ class TimedStat {
}
};
+namespace detail {
+template <class T, class Cmp>
+struct MinMaxStat {
+ using Event = T;
+ void on_event(Event event) {
+ if (!best_ || Cmp()(event, best_.value())) {
+ best_ = event;
+ }
+ }
+ optional<T> get_stat() const {
+ return best_.copy();
+ }
+
+ private:
+ optional<T> best_;
+};
+} // namespace detail
+
+template <class T>
+using MinStat = detail::MinMaxStat<T, std::less<void>>;
+
+template <class T>
+using MaxStat = detail::MinMaxStat<T, std::greater<void>>;
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.cpp
index dc35721caa..215e1664af 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,34 +8,65 @@
#include "td/utils/format.h"
#include "td/utils/logging.h"
-//#include "td/utils/Slice.h" // TODO move StringBuilder implementation to cpp, remove header
#include "td/utils/Time.h"
namespace td {
-Timer::Timer() : start_time_(Time::now()) {
+Timer::Timer(bool is_paused) {
+ if (!is_paused) {
+ resume();
+ }
+}
+
+void Timer::pause() {
+ if (is_paused_) {
+ return;
+ }
+ elapsed_ += Time::now() - start_time_;
+ is_paused_ = true;
+}
+
+void Timer::resume() {
+ if (!is_paused_) {
+ return;
+ }
+ start_time_ = Time::now();
+ is_paused_ = false;
+}
+
+double Timer::elapsed() const {
+ double res = elapsed_;
+ if (!is_paused_) {
+ res += Time::now() - start_time_;
+ }
+ return res;
}
StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer) {
- return string_builder << "in " << Time::now() - timer.start_time_;
+ return string_builder << " in " << format::as_time(timer.elapsed());
}
PerfWarningTimer::PerfWarningTimer(string name, double max_duration)
: name_(std::move(name)), start_at_(Time::now()), max_duration_(max_duration) {
}
-PerfWarningTimer::PerfWarningTimer(PerfWarningTimer &&other)
+PerfWarningTimer::PerfWarningTimer(PerfWarningTimer &&other) noexcept
: name_(std::move(other.name_)), start_at_(other.start_at_), max_duration_(other.max_duration_) {
other.start_at_ = 0;
}
PerfWarningTimer::~PerfWarningTimer() {
+ reset();
+}
+
+void PerfWarningTimer::reset() {
if (start_at_ == 0) {
return;
}
double duration = Time::now() - start_at_;
LOG_IF(WARNING, duration > max_duration_)
<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
+ start_at_ = 0;
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.h
index 65b879088d..bedab6ad4b 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Timer.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -12,12 +12,22 @@ namespace td {
class Timer {
public:
- Timer();
+ Timer() : Timer(false) {
+ }
+ explicit Timer(bool is_paused);
+
+ double elapsed() const;
+
+ void pause();
+
+ void resume();
private:
friend StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer);
- double start_time_;
+ double elapsed_{0};
+ double start_time_{0};
+ bool is_paused_{true};
};
class PerfWarningTimer {
@@ -25,9 +35,10 @@ class PerfWarningTimer {
explicit PerfWarningTimer(string name, double max_duration = 0.1);
PerfWarningTimer(const PerfWarningTimer &) = delete;
PerfWarningTimer &operator=(const PerfWarningTimer &) = delete;
- PerfWarningTimer(PerfWarningTimer &&other);
+ PerfWarningTimer(PerfWarningTimer &&other) noexcept;
PerfWarningTimer &operator=(PerfWarningTimer &&) = delete;
~PerfWarningTimer();
+ void reset();
private:
string name_;
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TlDowncastHelper.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/TlDowncastHelper.h
new file mode 100644
index 0000000000..3f0c09a1cf
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TlDowncastHelper.h
@@ -0,0 +1,29 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/TlStorerToString.h"
+
+namespace td {
+
+template <class T>
+class TlDowncastHelper final : public T {
+ public:
+ explicit TlDowncastHelper(int32 constructor) : constructor_(constructor) {
+ }
+ int32 get_id() const final {
+ return constructor_;
+ }
+ void store(TlStorerToString &s, const char *field_name) const final {
+ }
+
+ private:
+ int32 constructor_{0};
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TlStorerToString.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/TlStorerToString.h
new file mode 100644
index 0000000000..db246135bf
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TlStorerToString.h
@@ -0,0 +1,180 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/SharedSlice.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/UInt.h"
+
+namespace td {
+
+class TlStorerToString {
+ string result;
+ size_t shift = 0;
+
+ void store_field_begin(const char *name) {
+ result.append(shift, ' ');
+ if (name && name[0]) {
+ result += name;
+ result += " = ";
+ }
+ }
+
+ void store_field_end() {
+ result += '\n';
+ }
+
+ void store_long(int64 value) {
+ result += (PSLICE() << value).c_str();
+ }
+
+ void store_binary(Slice data) {
+ static const char *hex = "0123456789ABCDEF";
+
+ result.append("{ ", 2);
+ for (auto c : data) {
+ unsigned char byte = c;
+ result += hex[byte >> 4];
+ result += hex[byte & 15];
+ result += ' ';
+ }
+ result += '}';
+ }
+
+ public:
+ TlStorerToString() = default;
+ TlStorerToString(const TlStorerToString &other) = delete;
+ TlStorerToString &operator=(const TlStorerToString &other) = delete;
+
+ void store_field(const char *name, bool value) {
+ store_field_begin(name);
+ result += (value ? "true" : "false");
+ store_field_end();
+ }
+
+ void store_field(const char *name, int32 value) {
+ store_field(name, static_cast<int64>(value));
+ }
+
+ void store_field(const char *name, int64 value) {
+ store_field_begin(name);
+ store_long(value);
+ store_field_end();
+ }
+
+ void store_field(const char *name, double value) {
+ store_field_begin(name);
+ result += (PSLICE() << value).c_str();
+ store_field_end();
+ }
+
+ void store_field(const char *name, const char *value) {
+ store_field_begin(name);
+ result += value;
+ store_field_end();
+ }
+
+ void store_field(const char *name, const string &value) {
+ store_field_begin(name);
+ result += '"';
+ result += value;
+ result += '"';
+ store_field_end();
+ }
+
+ void store_field(const char *name, const SecureString &value) {
+ store_field_begin(name);
+ result.append("<secret>");
+ store_field_end();
+ }
+
+ template <class T>
+ void store_field(const char *name, const T &value) {
+ store_field_begin(name);
+ result.append(value.data(), value.size());
+ store_field_end();
+ }
+
+ void store_bytes_field(const char *name, const SecureString &value) {
+ store_field_begin(name);
+ result.append("<secret>");
+ store_field_end();
+ }
+
+ template <class BytesT>
+ void store_bytes_field(const char *name, const BytesT &value) {
+ static const char *hex = "0123456789ABCDEF";
+
+ store_field_begin(name);
+ result.append("bytes [");
+ store_long(static_cast<int64>(value.size()));
+ result.append("] { ");
+ size_t len = min(static_cast<size_t>(64), value.size());
+ for (size_t i = 0; i < len; i++) {
+ int b = value[static_cast<int>(i)] & 0xff;
+ result += hex[b >> 4];
+ result += hex[b & 15];
+ result += ' ';
+ }
+ if (len < value.size()) {
+ result.append("...");
+ }
+ result += '}';
+ store_field_end();
+ }
+
+ template <class ObjectT>
+ void store_object_field(const char *name, const ObjectT *value) {
+ if (value == nullptr) {
+ store_field(name, "null");
+ } else {
+ value->store(*this, name);
+ }
+ }
+
+ void store_field(const char *name, const UInt128 &value) {
+ store_field_begin(name);
+ store_binary(as_slice(value));
+ store_field_end();
+ }
+
+ void store_field(const char *name, const UInt256 &value) {
+ store_field_begin(name);
+ store_binary(as_slice(value));
+ store_field_end();
+ }
+
+ void store_vector_begin(const char *field_name, size_t vector_size) {
+ store_field_begin(field_name);
+ result += "vector[";
+ result += (PSLICE() << vector_size).c_str();
+ result += "] {\n";
+ shift += 2;
+ }
+
+ void store_class_begin(const char *field_name, const char *class_name) {
+ store_field_begin(field_name);
+ result += class_name;
+ result += " {\n";
+ shift += 2;
+ }
+
+ void store_class_end() {
+ CHECK(shift >= 2);
+ shift -= 2;
+ result.append(shift, ' ');
+ result += "}\n";
+ }
+
+ string move_as_string() {
+ return std::move(result);
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.cpp
new file mode 100644
index 0000000000..df7080b68d
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.cpp
@@ -0,0 +1,64 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/TsCerr.h"
+
+#include "td/utils/ExitGuard.h"
+#include "td/utils/port/StdStreams.h"
+#include "td/utils/Time.h"
+
+#include <cerrno>
+
+namespace td {
+
+std::atomic_flag TsCerr::lock_ = ATOMIC_FLAG_INIT;
+
+TsCerr::TsCerr() {
+ enterCritical();
+}
+
+TsCerr::~TsCerr() {
+ exitCritical();
+}
+
+TsCerr &TsCerr::operator<<(Slice slice) {
+ auto &fd = Stderr();
+ if (fd.empty()) {
+ return *this;
+ }
+ double end_time = 0;
+ while (!slice.empty()) {
+ auto res = fd.write(slice);
+ if (res.is_error()) {
+ if (res.error().code() == EPIPE) {
+ break;
+ }
+ // Resource temporary unavailable
+ if (end_time == 0) {
+ end_time = Time::now() + 0.01;
+ } else if (Time::now() > end_time) {
+ break;
+ }
+ continue;
+ }
+ slice.remove_prefix(res.ok());
+ }
+ return *this;
+}
+
+void TsCerr::enterCritical() {
+ while (lock_.test_and_set(std::memory_order_acquire) && !ExitGuard::is_exited()) {
+ // spin
+ }
+}
+
+void TsCerr::exitCritical() {
+ lock_.clear(std::memory_order_release);
+}
+
+static ExitGuard exit_guard;
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.h
new file mode 100644
index 0000000000..686003df3b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsCerr.h
@@ -0,0 +1,33 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/Slice.h"
+
+#include <atomic>
+
+namespace td {
+
+class TsCerr {
+ public:
+ TsCerr();
+ TsCerr(const TsCerr &) = delete;
+ TsCerr &operator=(const TsCerr &) = delete;
+ TsCerr(TsCerr &&) = delete;
+ TsCerr &operator=(TsCerr &&) = delete;
+ ~TsCerr();
+
+ TsCerr &operator<<(Slice slice);
+
+ private:
+ static std::atomic_flag lock_;
+
+ static void enterCritical();
+ static void exitCritical();
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.cpp
new file mode 100644
index 0000000000..44d9a420f7
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.cpp
@@ -0,0 +1,106 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/TsFileLog.h"
+
+#include "td/utils/common.h"
+#include "td/utils/FileLog.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/thread_local.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+
+#include <array>
+#include <atomic>
+#include <limits>
+#include <mutex>
+
+namespace td {
+
+namespace detail {
+class TsFileLog final : public LogInterface {
+ public:
+ Status init(string path, int64 rotate_threshold, bool redirect_stderr) {
+ path_ = std::move(path);
+ rotate_threshold_ = rotate_threshold;
+ redirect_stderr_ = redirect_stderr;
+ for (size_t i = 0; i < logs_.size(); i++) {
+ logs_[i].id = i;
+ }
+ return init_info(&logs_[0]);
+ }
+
+ void rotate() {
+ for (auto &info : logs_) {
+ if (info.is_inited.load(std::memory_order_acquire)) {
+ info.log.lazy_rotate();
+ }
+ }
+ }
+
+ private:
+ struct Info {
+ FileLog log;
+ std::atomic<bool> is_inited{false};
+ size_t id;
+ };
+
+ static constexpr size_t MAX_THREAD_ID = 128;
+ int64 rotate_threshold_ = 0;
+ bool redirect_stderr_ = false;
+ std::string path_;
+ std::array<Info, MAX_THREAD_ID> logs_;
+ std::mutex init_mutex_;
+
+ LogInterface *get_current_logger() {
+ auto *info = get_current_info();
+ if (!info->is_inited.load(std::memory_order_relaxed)) {
+ std::unique_lock<std::mutex> lock(init_mutex_);
+ if (!info->is_inited.load(std::memory_order_relaxed)) {
+ init_info(info).ensure();
+ }
+ }
+ return &info->log;
+ }
+
+ Info *get_current_info() {
+ return &logs_[get_thread_id()];
+ }
+
+ Status init_info(Info *info) {
+ TRY_STATUS(info->log.init(get_path(info), std::numeric_limits<int64>::max(), info->id == 0 && redirect_stderr_));
+ info->is_inited = true;
+ return Status::OK();
+ }
+
+ string get_path(const Info *info) const {
+ if (info->id == 0) {
+ return path_;
+ }
+ return PSTRING() << path_ << ".thread" << info->id << ".log";
+ }
+
+ void do_append(int log_level, CSlice slice) final {
+ get_current_logger()->do_append(log_level, slice);
+ }
+
+ vector<string> get_file_paths() final {
+ vector<string> res;
+ for (auto &log : logs_) {
+ res.push_back(get_path(&log));
+ }
+ return res;
+ }
+};
+} // namespace detail
+
+Result<unique_ptr<LogInterface>> TsFileLog::create(string path, int64 rotate_threshold, bool redirect_stderr) {
+ auto res = make_unique<detail::TsFileLog>();
+ TRY_STATUS(res->init(std::move(path), rotate_threshold, redirect_stderr));
+ return std::move(res);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.h
new file mode 100644
index 0000000000..2a84fec2a1
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsFileLog.h
@@ -0,0 +1,23 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/Status.h"
+
+namespace td {
+
+class TsFileLog {
+ static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
+
+ public:
+ static Result<unique_ptr<LogInterface>> create(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD,
+ bool redirect_stderr = true);
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TsList.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsList.h
new file mode 100644
index 0000000000..8302a6d500
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsList.h
@@ -0,0 +1,214 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/List.h"
+
+#include <mutex>
+
+namespace td {
+
+template <class DataT>
+class TsList;
+
+template <class DataT>
+class TsListNode : protected ListNode {
+ public:
+ TsListNode() {
+ clear();
+ }
+ explicit TsListNode(DataT &&data) : data_(std::move(data)) {
+ clear();
+ }
+
+ ~TsListNode() {
+ remove();
+ }
+
+ std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT;
+
+ TsListNode(const TsListNode &) = delete;
+ TsListNode &operator=(const TsListNode &) = delete;
+
+ TsListNode(TsListNode &&other) noexcept {
+ other.validate();
+ if (other.empty()) {
+ data_ = std::move(other.data_);
+ clear();
+ } else {
+ auto guard = other.lock();
+ init_from(std::move(other));
+ }
+ validate();
+ other.validate();
+ }
+
+ TsListNode &operator=(TsListNode &&other) noexcept {
+ validate();
+ if (this == &other) {
+ return *this;
+ }
+ other.validate();
+ remove();
+
+ if (other.empty()) {
+ data_ = std::move(other.data_);
+ } else {
+ auto guard = other.lock();
+ init_from(std::move(other));
+ }
+
+ validate();
+ other.validate();
+ return *this;
+ }
+
+ void validate() {
+ if (empty()) {
+ CHECK(ListNode::empty());
+ } else {
+ auto guard = lock();
+ CHECK(!ListNode::empty() || is_root);
+ }
+ }
+
+ void remove() {
+ validate();
+ if (is_root) {
+ CHECK(ListNode::empty());
+ return;
+ }
+ if (empty()) {
+ CHECK(ListNode::empty());
+ return;
+ }
+ {
+ auto guard = lock();
+ ListNode::remove();
+ if (!is_root) {
+ parent = nullptr;
+ }
+ }
+ validate();
+ }
+
+ void put(TsListNode *other) {
+ validate();
+ other->validate();
+ DCHECK(other->empty());
+ DCHECK(!empty());
+ DCHECK(!other->is_root);
+ {
+ auto guard = lock();
+ ListNode::put(other);
+ other->parent = parent;
+ }
+ validate();
+ other->validate();
+ }
+
+ void put_back(TsListNode *other) {
+ DCHECK(other->empty());
+ DCHECK(!empty());
+ DCHECK(!other->is_root);
+ auto guard = lock();
+ ListNode::put_back(other);
+ other->parent = parent;
+ }
+
+ bool empty() const {
+ return parent == nullptr;
+ }
+
+ TsListNode *get_next() {
+ return static_cast<TsListNode *>(next);
+ }
+ TsListNode *get_prev() {
+ return static_cast<TsListNode *>(prev);
+ }
+
+ DataT &get_data_unsafe() {
+ return data_;
+ }
+
+ private:
+ TsList<DataT> *parent;
+ bool is_root{false};
+ DataT data_;
+
+ friend class TsList<DataT>;
+
+ void clear() {
+ ListNode::clear();
+ if (!is_root) {
+ parent = nullptr;
+ }
+ }
+
+ void init_from(TsListNode &&other) {
+ ListNode::init_from(std::move(other));
+ parent = other.parent;
+ other.parent = nullptr;
+ data_ = std::move(other.data_);
+ }
+};
+
+template <class DataT>
+class TsList final : public TsListNode<DataT> {
+ public:
+ TsList() {
+ this->parent = this;
+ this->is_root = true;
+ }
+ TsList(const TsList &) = delete;
+ TsList &operator=(const TsList &) = delete;
+ TsList(TsList &&) = delete;
+ TsList &operator=(TsList &&) = delete;
+ ~TsList() {
+ auto guard = lock();
+ while (true) {
+ auto res = static_cast<TsListNode<DataT> *>(ListNode::get());
+ if (!res) {
+ break;
+ }
+ res->parent = nullptr;
+ }
+ this->parent = nullptr;
+ }
+ std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT {
+ return std::unique_lock<std::mutex>(mutex_);
+ }
+ TsListNode<DataT> *begin() {
+ return this->get_next();
+ }
+ TsListNode<DataT> *end() {
+ return this;
+ }
+ TsListNode<DataT> *get() {
+ auto guard = lock();
+ auto res = static_cast<TsListNode<DataT> *>(ListNode::get());
+ if (res) {
+ res->parent = nullptr;
+ }
+ return res;
+ }
+
+ private:
+ std::mutex mutex_;
+};
+
+template <class DataT>
+std::unique_lock<std::mutex> TsListNode<DataT>::lock() {
+ if (parent == nullptr) {
+ return {};
+ }
+ CHECK(parent != nullptr);
+ return parent->lock();
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.cpp
new file mode 100644
index 0000000000..0dba99de8b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.cpp
@@ -0,0 +1,21 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/TsLog.h"
+
+#include "td/utils/ExitGuard.h"
+
+namespace td {
+
+void TsLog::enter_critical() {
+ while (lock_.test_and_set(std::memory_order_acquire) && !ExitGuard::is_exited()) {
+ // spin
+ }
+}
+
+static ExitGuard exit_guard;
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.h
new file mode 100644
index 0000000000..7ef9f2c4b9
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/TsLog.h
@@ -0,0 +1,55 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/Slice.h"
+
+#include <atomic>
+
+namespace td {
+
+class TsLog final : public LogInterface {
+ public:
+ explicit TsLog(LogInterface *log) : log_(log) {
+ }
+ void init(LogInterface *log) {
+ enter_critical();
+ log_ = log;
+ exit_critical();
+ }
+ void after_rotation() final {
+ enter_critical();
+ log_->after_rotation();
+ exit_critical();
+ }
+ vector<string> get_file_paths() final {
+ enter_critical();
+ auto result = log_->get_file_paths();
+ exit_critical();
+ return result;
+ }
+
+ private:
+ void do_append(int log_level, CSlice slice) final {
+ enter_critical();
+ log_->do_append(log_level, slice);
+ exit_critical();
+ }
+
+ void enter_critical();
+
+ void exit_critical() {
+ lock_.clear(std::memory_order_release);
+ }
+
+ LogInterface *log_ = nullptr;
+ std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/UInt.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/UInt.h
new file mode 100644
index 0000000000..03714d7a4f
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/UInt.h
@@ -0,0 +1,91 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Slice.h"
+
+namespace td {
+
+template <size_t size>
+struct UInt {
+ static_assert(size % 8 == 0, "size should be divisible by 8");
+ uint8 raw[size / 8];
+
+ Slice as_slice() const {
+ return Slice(raw, size / 8);
+ }
+
+ MutableSlice as_slice() {
+ return MutableSlice(raw, size / 8);
+ }
+
+ bool is_zero() const {
+ for (size_t i = 0; i < size / 8; i++) {
+ if (raw[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+ void set_zero() {
+ for (size_t i = 0; i < size / 8; i++) {
+ raw[i] = 0;
+ }
+ }
+ static UInt zero() {
+ UInt v;
+ v.set_zero();
+ return v;
+ }
+};
+
+template <size_t size>
+bool operator==(const UInt<size> &a, const UInt<size> &b) {
+ return a.as_slice() == b.as_slice();
+}
+
+template <size_t size>
+bool operator!=(const UInt<size> &a, const UInt<size> &b) {
+ return !(a == b);
+}
+
+template <size_t size>
+UInt<size> operator^(const UInt<size> &a, const UInt<size> &b) {
+ UInt<size> res;
+ for (size_t i = 0; i < size / 8; i++) {
+ res.raw[i] = static_cast<uint8>(a.raw[i] ^ b.raw[i]);
+ }
+ return res;
+}
+
+template <size_t size>
+int get_kth_bit(const UInt<size> &a, uint32 bit) {
+ uint8 b = a.raw[bit / 8];
+ bit &= 7;
+ return (b >> (7 - bit)) & 1;
+}
+
+template <size_t size>
+Slice as_slice(const UInt<size> &value) {
+ return value.as_slice();
+}
+
+template <size_t size>
+MutableSlice as_slice(UInt<size> &value) {
+ return value.as_slice();
+}
+
+template <size_t size>
+bool operator<(const UInt<size> &a, const UInt<size> &b) {
+ return a.as_slice() < b.as_slice();
+}
+
+using UInt128 = UInt<128>;
+using UInt256 = UInt<256>;
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/Variant.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/Variant.h
index 9b6e0561cc..9d064265fb 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/Variant.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/Variant.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -55,7 +55,7 @@ class IthTypeImpl<pos, Skip, Args...> : public IthTypeImpl<pos - 1, Args...> {};
class Dummy {};
template <size_t pos, class... Args>
-class IthType : public IthTypeImpl<pos, Args..., Dummy> {};
+class IthType final : public IthTypeImpl<pos, Args..., Dummy> {};
template <bool ok, int offset, class... Types>
class FindTypeOffsetImpl {};
@@ -69,7 +69,7 @@ template <int offset, class T, class S, class... Types>
class FindTypeOffsetImpl<false, offset, T, S, Types...>
: public FindTypeOffsetImpl<std::is_same<T, S>::value, offset + 1, T, Types...> {};
template <class T, class... Types>
-class FindTypeOffset : public FindTypeOffsetImpl<false, -1, T, Types...> {};
+class FindTypeOffset final : public FindTypeOffsetImpl<false, -1, T, Types...> {};
template <int offset, class... Types>
class ForEachTypeImpl {};
@@ -109,18 +109,21 @@ class Variant {
static constexpr int npos = -1;
Variant() {
}
- Variant(Variant &&other) {
+ Variant(Variant &&other) noexcept {
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
}
Variant(const Variant &other) {
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
}
- Variant &operator=(Variant &&other) {
+ Variant &operator=(Variant &&other) noexcept {
clear();
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
return *this;
}
Variant &operator=(const Variant &other) {
+ if (this == &other) {
+ return *this;
+ }
clear();
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
return *this;
@@ -153,11 +156,11 @@ class Variant {
return res;
}
- template <class T>
+ template <class T, std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value, int> = 0>
Variant(T &&t) {
init_empty(std::forward<T>(t));
}
- template <class T>
+ template <class T, std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value, int> = 0>
Variant &operator=(T &&t) {
clear();
init_empty(std::forward<T>(t));
@@ -170,7 +173,11 @@ class Variant {
template <class T>
void init_empty(T &&t) {
- CHECK(offset_ == npos);
+ LOG_CHECK(offset_ == npos) << offset_
+#if TD_CLANG || TD_GCC
+ << ' ' << __PRETTY_FUNCTION__
+#endif
+ ;
offset_ = offset<T>();
new (&get<T>()) std::decay_t<T>(std::forward<T>(t));
}
@@ -237,6 +244,10 @@ class Variant {
return offset_;
}
+ bool empty() const {
+ return offset_ == npos;
+ }
+
private:
union {
int64 align_;
@@ -283,4 +294,5 @@ template <int T, class... Types>
auto &get(const Variant<Types...> &v) {
return v.template get<T>();
}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/VectorQueue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/VectorQueue.h
new file mode 100644
index 0000000000..c67a440caf
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/VectorQueue.h
@@ -0,0 +1,94 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Span.h"
+
+#include <utility>
+
+namespace td {
+
+template <class T>
+class VectorQueue {
+ public:
+ template <class S>
+ void push(S &&s) {
+ vector_.emplace_back(std::forward<S>(s));
+ }
+
+ template <class... Args>
+ void emplace(Args &&...args) {
+ vector_.emplace_back(std::forward<Args>(args)...);
+ }
+
+ T pop() {
+ try_shrink();
+ return std::move(vector_[read_pos_++]);
+ }
+
+ template <class RndT>
+ T pop_rand(RndT &rnd) {
+ auto i = rnd() % size();
+ std::swap(vector_[i], vector_[read_pos_]);
+ return pop();
+ }
+
+ void pop_n(size_t n) {
+ read_pos_ += n;
+ try_shrink();
+ }
+
+ const T &front() const {
+ return vector_[read_pos_];
+ }
+ T &front() {
+ return vector_[read_pos_];
+ }
+
+ const T &back() const {
+ return vector_.back();
+ }
+ T &back() {
+ return vector_.back();
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+
+ size_t size() const {
+ return vector_.size() - read_pos_;
+ }
+
+ const T *data() const {
+ return vector_.data() + read_pos_;
+ }
+ T *data() {
+ return vector_.data() + read_pos_;
+ }
+
+ Span<T> as_span() const {
+ return {data(), size()};
+ }
+ MutableSpan<T> as_mutable_span() {
+ return {vector_.data() + read_pos_, size()};
+ }
+
+ private:
+ vector<T> vector_;
+ size_t read_pos_{0};
+
+ void try_shrink() {
+ if (read_pos_ * 2 > vector_.size() && read_pos_ > 4) {
+ vector_.erase(vector_.begin(), vector_.begin() + read_pos_);
+ read_pos_ = 0;
+ }
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashMap.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashMap.h
new file mode 100644
index 0000000000..2b34bebaa1
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashMap.h
@@ -0,0 +1,190 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/FlatHashMap.h"
+#include "td/utils/HashTableUtils.h"
+
+#include <functional>
+
+namespace td {
+
+template <class KeyT, class ValueT, class HashT = Hash<KeyT>, class EqT = std::equal_to<KeyT>>
+class WaitFreeHashMap {
+ static constexpr size_t MAX_STORAGE_COUNT = 1 << 8;
+ static_assert((MAX_STORAGE_COUNT & (MAX_STORAGE_COUNT - 1)) == 0, "");
+ static constexpr uint32 DEFAULT_STORAGE_SIZE = 1 << 12;
+
+ FlatHashMap<KeyT, ValueT, HashT, EqT> default_map_;
+ struct WaitFreeStorage {
+ WaitFreeHashMap maps_[MAX_STORAGE_COUNT];
+ };
+ unique_ptr<WaitFreeStorage> wait_free_storage_;
+ uint32 hash_mult_ = 1;
+ uint32 max_storage_size_ = DEFAULT_STORAGE_SIZE;
+
+ uint32 get_wait_free_index(const KeyT &key) const {
+ return randomize_hash(HashT()(key) * hash_mult_) & (MAX_STORAGE_COUNT - 1);
+ }
+
+ WaitFreeHashMap &get_wait_free_storage(const KeyT &key) {
+ return wait_free_storage_->maps_[get_wait_free_index(key)];
+ }
+
+ const WaitFreeHashMap &get_wait_free_storage(const KeyT &key) const {
+ return wait_free_storage_->maps_[get_wait_free_index(key)];
+ }
+
+ void split_storage() {
+ CHECK(wait_free_storage_ == nullptr);
+ wait_free_storage_ = make_unique<WaitFreeStorage>();
+ uint32 next_hash_mult = hash_mult_ * 1000000007;
+ for (uint32 i = 0; i < MAX_STORAGE_COUNT; i++) {
+ auto &map = wait_free_storage_->maps_[i];
+ map.hash_mult_ = next_hash_mult;
+ map.max_storage_size_ = DEFAULT_STORAGE_SIZE + i * next_hash_mult % DEFAULT_STORAGE_SIZE;
+ }
+ for (auto &it : default_map_) {
+ get_wait_free_storage(it.first).set(it.first, std::move(it.second));
+ }
+ default_map_.clear();
+ }
+
+ public:
+ void set(const KeyT &key, ValueT value) {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).set(key, std::move(value));
+ }
+
+ default_map_[key] = std::move(value);
+ if (default_map_.size() == max_storage_size_) {
+ split_storage();
+ }
+ }
+
+ ValueT get(const KeyT &key) const {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).get(key);
+ }
+
+ auto it = default_map_.find(key);
+ if (it == default_map_.end()) {
+ return {};
+ }
+ return it->second;
+ }
+
+ size_t count(const KeyT &key) const {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).count(key);
+ }
+
+ return default_map_.count(key);
+ }
+
+ // specialization for WaitFreeHashMap<..., unique_ptr<T>>
+ template <class T = ValueT>
+ typename T::element_type *get_pointer(const KeyT &key) {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).get_pointer(key);
+ }
+
+ auto it = default_map_.find(key);
+ if (it == default_map_.end()) {
+ return nullptr;
+ }
+ return it->second.get();
+ }
+
+ template <class T = ValueT>
+ const typename T::element_type *get_pointer(const KeyT &key) const {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).get_pointer(key);
+ }
+
+ auto it = default_map_.find(key);
+ if (it == default_map_.end()) {
+ return nullptr;
+ }
+ return it->second.get();
+ }
+
+ ValueT &operator[](const KeyT &key) {
+ if (wait_free_storage_ == nullptr) {
+ ValueT &result = default_map_[key];
+ if (default_map_.size() != max_storage_size_) {
+ return result;
+ }
+
+ split_storage();
+ }
+
+ return get_wait_free_storage(key)[key];
+ }
+
+ size_t erase(const KeyT &key) {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).erase(key);
+ }
+
+ return default_map_.erase(key);
+ }
+
+ void foreach(const std::function<void(const KeyT &key, ValueT &value)> &callback) {
+ if (wait_free_storage_ == nullptr) {
+ for (auto &it : default_map_) {
+ callback(it.first, it.second);
+ }
+ return;
+ }
+
+ for (auto &it : wait_free_storage_->maps_) {
+ it.foreach(callback);
+ }
+ }
+
+ void foreach(const std::function<void(const KeyT &key, const ValueT &value)> &callback) const {
+ if (wait_free_storage_ == nullptr) {
+ for (auto &it : default_map_) {
+ callback(it.first, it.second);
+ }
+ return;
+ }
+
+ for (auto &it : wait_free_storage_->maps_) {
+ it.foreach(callback);
+ }
+ }
+
+ size_t calc_size() const {
+ if (wait_free_storage_ == nullptr) {
+ return default_map_.size();
+ }
+
+ size_t result = 0;
+ for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
+ result += wait_free_storage_->maps_[i].calc_size();
+ }
+ return result;
+ }
+
+ bool empty() const {
+ if (wait_free_storage_ == nullptr) {
+ return default_map_.empty();
+ }
+
+ for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
+ if (!wait_free_storage_->maps_[i].empty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashSet.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashSet.h
new file mode 100644
index 0000000000..5fddc0903d
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeHashSet.h
@@ -0,0 +1,141 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/FlatHashSet.h"
+#include "td/utils/HashTableUtils.h"
+
+#include <functional>
+
+namespace td {
+
+template <class KeyT, class HashT = Hash<KeyT>, class EqT = std::equal_to<KeyT>>
+class WaitFreeHashSet {
+ static constexpr size_t MAX_STORAGE_COUNT = 1 << 8;
+ static_assert((MAX_STORAGE_COUNT & (MAX_STORAGE_COUNT - 1)) == 0, "");
+ static constexpr uint32 DEFAULT_STORAGE_SIZE = 1 << 12;
+
+ FlatHashSet<KeyT, HashT, EqT> default_set_;
+ struct WaitFreeStorage {
+ WaitFreeHashSet sets_[MAX_STORAGE_COUNT];
+ };
+ unique_ptr<WaitFreeStorage> wait_free_storage_;
+ uint32 hash_mult_ = 1;
+ uint32 max_storage_size_ = DEFAULT_STORAGE_SIZE;
+
+ uint32 get_wait_free_index(const KeyT &key) const {
+ return randomize_hash(HashT()(key) * hash_mult_) & (MAX_STORAGE_COUNT - 1);
+ }
+
+ WaitFreeHashSet &get_wait_free_storage(const KeyT &key) {
+ return wait_free_storage_->sets_[get_wait_free_index(key)];
+ }
+
+ const WaitFreeHashSet &get_wait_free_storage(const KeyT &key) const {
+ return wait_free_storage_->sets_[get_wait_free_index(key)];
+ }
+
+ void split_storage() {
+ CHECK(wait_free_storage_ == nullptr);
+ wait_free_storage_ = make_unique<WaitFreeStorage>();
+ uint32 next_hash_mult = hash_mult_ * 1000000007;
+ for (uint32 i = 0; i < MAX_STORAGE_COUNT; i++) {
+ auto &set = wait_free_storage_->sets_[i];
+ set.hash_mult_ = next_hash_mult;
+ set.max_storage_size_ = DEFAULT_STORAGE_SIZE + i * next_hash_mult % DEFAULT_STORAGE_SIZE;
+ }
+ for (auto &it : default_set_) {
+ get_wait_free_storage(it).insert(it);
+ }
+ default_set_.clear();
+ }
+
+ public:
+ void insert(const KeyT &key) {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).insert(key);
+ }
+
+ default_set_.insert(key);
+ if (default_set_.size() == max_storage_size_) {
+ split_storage();
+ }
+ }
+
+ size_t count(const KeyT &key) const {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).count(key);
+ }
+
+ return default_set_.count(key);
+ }
+
+ size_t erase(const KeyT &key) {
+ if (wait_free_storage_ != nullptr) {
+ return get_wait_free_storage(key).erase(key);
+ }
+
+ return default_set_.erase(key);
+ }
+
+ void foreach(const std::function<void(const KeyT &key)> &callback) const {
+ if (wait_free_storage_ == nullptr) {
+ for (auto &it : default_set_) {
+ callback(it);
+ }
+ return;
+ }
+
+ for (auto &it : wait_free_storage_->sets_) {
+ it.foreach(callback);
+ }
+ }
+
+ KeyT get_random() const {
+ if (wait_free_storage_ != nullptr) {
+ for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
+ if (!wait_free_storage_->sets_[i].empty()) {
+ return wait_free_storage_->sets_[i].get_random();
+ }
+ }
+ // no need to explicitly return KeyT()
+ }
+
+ if (default_set_.empty()) {
+ return KeyT();
+ }
+ return *default_set_.begin();
+ }
+
+ size_t calc_size() const {
+ if (wait_free_storage_ == nullptr) {
+ return default_set_.size();
+ }
+
+ size_t result = 0;
+ for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
+ result += wait_free_storage_->sets_[i].calc_size();
+ }
+ return result;
+ }
+
+ bool empty() const {
+ if (wait_free_storage_ == nullptr) {
+ return default_set_.empty();
+ }
+
+ for (size_t i = 0; i < MAX_STORAGE_COUNT; i++) {
+ if (!wait_free_storage_->sets_[i].empty()) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeVector.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeVector.h
new file mode 100644
index 0000000000..40ffd41c6b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/WaitFreeVector.h
@@ -0,0 +1,69 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#include <utility>
+
+namespace td {
+
+template <class T>
+class WaitFreeVector {
+ static constexpr size_t MAX_VECTOR_SIZE = (1 << 15) - 10;
+
+ vector<vector<T>> storage_;
+
+ public:
+ template <class... ArgsT>
+ void emplace_back(ArgsT &&...args) {
+ if (storage_.empty() || storage_.back().size() == MAX_VECTOR_SIZE) {
+ storage_.emplace_back();
+ }
+ storage_.back().emplace_back(std::forward<ArgsT>(args)...);
+ }
+
+ void pop_back() {
+ storage_.back().pop_back();
+ if (storage_.back().empty()) {
+ storage_.pop_back();
+ }
+ }
+
+ void push_back(T &&value) {
+ emplace_back(std::move(value));
+ }
+
+ void push_back(const T &value) {
+ emplace_back(value);
+ }
+
+ const T &back() const {
+ return storage_.back().back();
+ }
+
+ T &operator[](size_t index) {
+ return storage_[index / MAX_VECTOR_SIZE][index % MAX_VECTOR_SIZE];
+ }
+
+ const T &operator[](size_t index) const {
+ return storage_[index / MAX_VECTOR_SIZE][index % MAX_VECTOR_SIZE];
+ }
+
+ size_t size() const {
+ if (storage_.empty()) {
+ return 0;
+ }
+ return (storage_.size() - 1) * MAX_VECTOR_SIZE + storage_.back().size();
+ }
+
+ bool empty() const {
+ return storage_.empty();
+ }
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/algorithm.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/algorithm.h
new file mode 100644
index 0000000000..0c3702d8c7
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/algorithm.h
@@ -0,0 +1,217 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+namespace td {
+
+namespace detail {
+
+template <typename V>
+struct transform_helper {
+ template <class Func>
+ auto transform(const V &v, const Func &f) {
+ vector<decltype(f(*v.begin()))> result;
+ result.reserve(v.size());
+ for (auto &x : v) {
+ result.push_back(f(x));
+ }
+ return result;
+ }
+
+ template <class Func>
+ auto transform(V &&v, const Func &f) {
+ vector<decltype(f(std::move(*v.begin())))> result;
+ result.reserve(v.size());
+ for (auto &x : v) {
+ result.push_back(f(std::move(x)));
+ }
+ return result;
+ }
+};
+
+} // namespace detail
+
+template <class V, class Func>
+auto transform(V &&v, const Func &f) {
+ return detail::transform_helper<std::decay_t<V>>().transform(std::forward<V>(v), f);
+}
+
+template <class V, class Func>
+bool remove_if(V &v, const Func &f) {
+ size_t i = 0;
+ while (i != v.size() && !f(v[i])) {
+ i++;
+ }
+ if (i == v.size()) {
+ return false;
+ }
+
+ size_t j = i;
+ while (++i != v.size()) {
+ if (!f(v[i])) {
+ v[j++] = std::move(v[i]);
+ }
+ }
+ v.erase(v.begin() + j, v.end());
+ return true;
+}
+
+template <class V, class T>
+bool remove(V &v, const T &value) {
+ size_t i = 0;
+ while (i != v.size() && v[i] != value) {
+ i++;
+ }
+ if (i == v.size()) {
+ return false;
+ }
+
+ size_t j = i;
+ while (++i != v.size()) {
+ if (v[i] != value) {
+ v[j++] = std::move(v[i]);
+ }
+ }
+ v.erase(v.begin() + j, v.end());
+ return true;
+}
+
+template <class V>
+void unique(V &v) {
+ if (v.empty()) {
+ return;
+ }
+
+ // use ADL to find std::sort
+ // caller will need to #include <algorithm>
+ sort(v.begin(), v.end(), std::less<void>());
+
+ size_t j = 1;
+ for (size_t i = 1; i < v.size(); i++) {
+ if (v[i] != v[j - 1]) {
+ if (i != j) {
+ v[j] = std::move(v[i]);
+ }
+ j++;
+ }
+ }
+ v.resize(j);
+}
+
+template <class V, class T>
+bool contains(const V &v, const T &value) {
+ for (auto &x : v) {
+ if (x == value) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <class V, class F>
+bool all_of(const V &v, F &&f) {
+ for (const auto &x : v) {
+ if (!f(x)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <class T>
+void reset_to_empty(T &value) {
+ using std::swap;
+ std::decay_t<T> tmp;
+ swap(tmp, value);
+}
+
+template <class T>
+void append(vector<T> &destination, const vector<T> &source) {
+ destination.insert(destination.end(), source.begin(), source.end());
+}
+
+template <class T>
+void append(vector<T> &destination, vector<T> &&source) {
+ if (destination.empty()) {
+ destination.swap(source);
+ return;
+ }
+ destination.reserve(destination.size() + source.size());
+ for (auto &elem : source) {
+ destination.push_back(std::move(elem));
+ }
+ reset_to_empty(source);
+}
+
+template <class T>
+void combine(vector<T> &destination, const vector<T> &source) {
+ append(destination, source);
+}
+
+template <class T>
+void combine(vector<T> &destination, vector<T> &&source) {
+ if (destination.size() < source.size()) {
+ destination.swap(source);
+ }
+ if (source.empty()) {
+ return;
+ }
+ destination.reserve(destination.size() + source.size());
+ for (auto &elem : source) {
+ destination.push_back(std::move(elem));
+ }
+ reset_to_empty(source);
+}
+
+namespace detail {
+template <typename T>
+struct reversion_wrapper {
+ T &iterable;
+};
+
+template <typename T>
+auto begin(reversion_wrapper<T> w) {
+ return w.iterable.rbegin();
+}
+
+template <typename T>
+auto end(reversion_wrapper<T> w) {
+ return w.iterable.rend();
+}
+} // namespace detail
+
+template <typename T>
+detail::reversion_wrapper<T> reversed(T &iterable) {
+ return {iterable};
+}
+
+template <class TableT, class FuncT>
+void table_remove_if(TableT &table, FuncT &&func) {
+ for (auto it = table.begin(); it != table.end();) {
+ if (func(*it)) {
+ it = table.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+template <class NodeT, class HashT, class EqT>
+class FlatHashTable;
+
+template <class NodeT, class HashT, class EqT, class FuncT>
+void table_remove_if(FlatHashTable<NodeT, HashT, EqT> &table, FuncT &&func) {
+ table.remove_if(func);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/as.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/as.h
new file mode 100644
index 0000000000..e73c4a1b3b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/as.h
@@ -0,0 +1,81 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/type_traits.h"
+
+#include <cstring>
+#include <type_traits>
+
+namespace td {
+
+namespace detail {
+
+template <class T>
+class As {
+ public:
+ explicit As(void *ptr) : ptr_(ptr) {
+ }
+
+ As(const As &new_value) = delete;
+ As &operator=(const As &) = delete;
+ As(As &&) = default;
+ As &operator=(As &&new_value) &&noexcept {
+ std::memcpy(ptr_, new_value.ptr_, sizeof(T));
+ return *this;
+ }
+ ~As() = default;
+
+ As &operator=(const T &new_value) && {
+ std::memcpy(ptr_, &new_value, sizeof(T));
+ return *this;
+ }
+
+ operator T() const {
+ T res;
+ std::memcpy(&res, ptr_, sizeof(T));
+ return res;
+ }
+ bool operator==(const As &other) const {
+ return this->operator T() == other.operator T();
+ }
+
+ private:
+ void *ptr_;
+};
+
+template <class T>
+class ConstAs {
+ public:
+ explicit ConstAs(const void *ptr) : ptr_(ptr) {
+ }
+
+ operator T() const {
+ T res;
+ std::memcpy(&res, ptr_, sizeof(T));
+ return res;
+ }
+
+ private:
+ const void *ptr_;
+};
+
+} // namespace detail
+
+template <class ToT, class FromT,
+ std::enable_if_t<TD_IS_TRIVIALLY_COPYABLE(ToT) && TD_IS_TRIVIALLY_COPYABLE(FromT), int> = 0>
+detail::As<ToT> as(FromT *from) {
+ return detail::As<ToT>(from);
+}
+
+template <class ToT, class FromT,
+ std::enable_if_t<TD_IS_TRIVIALLY_COPYABLE(ToT) && TD_IS_TRIVIALLY_COPYABLE(FromT), int> = 0>
+detail::ConstAs<ToT> as(const FromT *from) {
+ return detail::ConstAs<ToT>(from);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.cpp
index 4016feaa58..2e06a21b86 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,6 @@
#include "td/utils/base64.h"
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
@@ -15,57 +14,68 @@
#include <iterator>
namespace td {
-//TODO: fix copypaste
-static const char *const symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+template <bool is_url>
+static const char *get_characters() {
+ return is_url ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
+ : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+}
-string base64_encode(Slice input) {
+template <bool is_url>
+static const unsigned char *get_character_table() {
+ static unsigned char char_to_value[256];
+ static bool is_inited = [] {
+ auto characters = get_characters<is_url>();
+ std::fill(std::begin(char_to_value), std::end(char_to_value), static_cast<unsigned char>(64));
+ for (unsigned char i = 0; i < 64; i++) {
+ char_to_value[static_cast<size_t>(characters[i])] = i;
+ }
+ return true;
+ }();
+ CHECK(is_inited);
+ return char_to_value;
+}
+
+template <bool is_url>
+string base64_encode_impl(Slice input) {
+ auto characters = get_characters<is_url>();
string base64;
base64.reserve((input.size() + 2) / 3 * 4);
for (size_t i = 0; i < input.size();) {
size_t left = min(input.size() - i, static_cast<size_t>(3));
int c = input.ubegin()[i++] << 16;
- base64 += symbols64[c >> 18];
+ base64 += characters[c >> 18];
if (left != 1) {
c |= input.ubegin()[i++] << 8;
}
- base64 += symbols64[(c >> 12) & 63];
+ base64 += characters[(c >> 12) & 63];
if (left == 3) {
c |= input.ubegin()[i++];
}
if (left != 1) {
- base64 += symbols64[(c >> 6) & 63];
- } else {
+ base64 += characters[(c >> 6) & 63];
+ } else if (!is_url) {
base64 += '=';
}
if (left == 3) {
- base64 += symbols64[c & 63];
- } else {
+ base64 += characters[c & 63];
+ } else if (!is_url) {
base64 += '=';
}
}
return base64;
}
-static unsigned char char_to_value[256];
-static void init_base64_table() {
- static bool is_inited = []() {
- std::fill(std::begin(char_to_value), std::end(char_to_value), 64);
- for (unsigned char i = 0; i < 64; i++) {
- char_to_value[static_cast<size_t>(symbols64[i])] = i;
- }
- return true;
- }();
- CHECK(is_inited);
+string base64_encode(Slice input) {
+ return base64_encode_impl<false>(input);
}
-Result<string> base64_decode(Slice base64) {
- init_base64_table();
-
- if ((base64.size() & 3) != 0) {
- return Status::Error("Wrong string length");
- }
+string base64url_encode(Slice input) {
+ return base64_encode_impl<true>(input);
+}
+template <bool is_url>
+Result<Slice> base64_drop_padding(Slice base64) {
size_t padding_length = 0;
while (!base64.empty() && base64.back() == '=') {
base64.remove_suffix(1);
@@ -74,121 +84,80 @@ Result<string> base64_decode(Slice base64) {
if (padding_length >= 3) {
return Status::Error("Wrong string padding");
}
+ if ((!is_url || padding_length > 0) && ((base64.size() + padding_length) & 3) != 0) {
+ return Status::Error("Wrong padding length");
+ }
+ if (is_url && (base64.size() & 3) == 1) {
+ return Status::Error("Wrong string length");
+ }
+ return base64;
+}
- string output;
- output.reserve(((base64.size() + 3) >> 2) * 3);
+static Status do_base64_decode_impl(Slice base64, const unsigned char *table, char *ptr) {
for (size_t i = 0; i < base64.size();) {
size_t left = min(base64.size() - i, static_cast<size_t>(4));
int c = 0;
for (size_t t = 0; t < left; t++) {
- auto value = char_to_value[base64.ubegin()[i++]];
+ auto value = table[base64.ubegin()[i++]];
if (value == 64) {
return Status::Error("Wrong character in the string");
}
c |= value << ((3 - t) * 6);
}
- output += static_cast<char>(static_cast<unsigned char>(c >> 16)); // implementation-defined
+ *ptr++ = static_cast<char>(static_cast<unsigned char>(c >> 16)); // implementation-defined
if (left == 2) {
if ((c & ((1 << 16) - 1)) != 0) {
return Status::Error("Wrong padding in the string");
}
} else {
- output += static_cast<char>(static_cast<unsigned char>(c >> 8)); // implementation-defined
+ *ptr++ = static_cast<char>(static_cast<unsigned char>(c >> 8)); // implementation-defined
if (left == 3) {
if ((c & ((1 << 8) - 1)) != 0) {
return Status::Error("Wrong padding in the string");
}
} else {
- output += static_cast<char>(static_cast<unsigned char>(c)); // implementation-defined
+ *ptr++ = static_cast<char>(static_cast<unsigned char>(c)); // implementation-defined
}
}
}
- return output;
+ return Status::OK();
}
-static const char *const url_symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+template <class T>
+static T create_empty(size_t size);
-string base64url_encode(Slice input) {
- string base64;
- base64.reserve((input.size() + 2) / 3 * 4);
- for (size_t i = 0; i < input.size();) {
- size_t left = min(input.size() - i, static_cast<size_t>(3));
- int c = input.ubegin()[i++] << 16;
- base64 += url_symbols64[c >> 18];
- if (left != 1) {
- c |= input.ubegin()[i++] << 8;
- }
- base64 += url_symbols64[(c >> 12) & 63];
- if (left == 3) {
- c |= input.ubegin()[i++];
- }
- if (left != 1) {
- base64 += url_symbols64[(c >> 6) & 63];
- }
- if (left == 3) {
- base64 += url_symbols64[c & 63];
- }
- }
- return base64;
+template <>
+string create_empty<string>(size_t size) {
+ return string(size, '\0');
}
-static unsigned char url_char_to_value[256];
-static void init_base64url_table() {
- static bool is_inited = []() {
- std::fill(std::begin(url_char_to_value), std::end(url_char_to_value), 64);
- for (unsigned char i = 0; i < 64; i++) {
- url_char_to_value[static_cast<size_t>(url_symbols64[i])] = i;
- }
- return true;
- }();
- CHECK(is_inited);
+template <>
+SecureString create_empty<SecureString>(size_t size) {
+ return SecureString{size};
}
-Result<string> base64url_decode(Slice base64) {
- init_base64url_table();
+template <bool is_url, class T>
+static Result<T> base64_decode_impl(Slice base64) {
+ TRY_RESULT_ASSIGN(base64, base64_drop_padding<is_url>(base64));
- size_t padding_length = 0;
- while (!base64.empty() && base64.back() == '=') {
- base64.remove_suffix(1);
- padding_length++;
- }
- if (padding_length >= 3 || (padding_length > 0 && ((base64.size() + padding_length) & 3) != 0)) {
- return Status::Error("Wrong string padding");
- }
+ T result = create_empty<T>(base64.size() / 4 * 3 + ((base64.size() & 3) + 1) / 2);
+ TRY_STATUS(do_base64_decode_impl(base64, get_character_table<is_url>(), as_mutable_slice(result).begin()));
+ return std::move(result);
+}
- if ((base64.size() & 3) == 1) {
- return Status::Error("Wrong string length");
- }
+Result<string> base64_decode(Slice base64) {
+ return base64_decode_impl<false, string>(base64);
+}
- string output;
- output.reserve(((base64.size() + 3) >> 2) * 3);
- for (size_t i = 0; i < base64.size();) {
- size_t left = min(base64.size() - i, static_cast<size_t>(4));
- int c = 0;
- for (size_t t = 0; t < left; t++) {
- auto value = url_char_to_value[base64.ubegin()[i++]];
- if (value == 64) {
- return Status::Error("Wrong character in the string");
- }
- c |= value << ((3 - t) * 6);
- }
- output += static_cast<char>(static_cast<unsigned char>(c >> 16)); // implementation-defined
- if (left == 2) {
- if ((c & ((1 << 16) - 1)) != 0) {
- return Status::Error("Wrong padding in the string");
- }
- } else {
- output += static_cast<char>(static_cast<unsigned char>(c >> 8)); // implementation-defined
- if (left == 3) {
- if ((c & ((1 << 8) - 1)) != 0) {
- return Status::Error("Wrong padding in the string");
- }
- } else {
- output += static_cast<char>(static_cast<unsigned char>(c)); // implementation-defined
- }
- }
- }
- return output;
+Result<SecureString> base64_decode_secure(Slice base64) {
+ return base64_decode_impl<false, SecureString>(base64);
+}
+
+Result<string> base64url_decode(Slice base64) {
+ return base64_decode_impl<true, string>(base64);
+}
+Result<SecureString> base64url_decode_secure(Slice base64) {
+ return base64_decode_impl<true, SecureString>(base64);
}
template <bool is_url>
@@ -208,14 +177,7 @@ static bool is_base64_impl(Slice input) {
return false;
}
- unsigned char *table;
- if (is_url) {
- init_base64url_table();
- table = url_char_to_value;
- } else {
- init_base64_table();
- table = char_to_value;
- }
+ auto table = get_character_table<is_url>();
for (auto c : input) {
if (table[static_cast<unsigned char>(c)] == 64) {
return false;
@@ -223,13 +185,13 @@ static bool is_base64_impl(Slice input) {
}
if ((input.size() & 3) == 2) {
- auto value = table[static_cast<int>(input.back())];
+ auto value = table[static_cast<unsigned char>(input.back())];
if ((value & 15) != 0) {
return false;
}
}
if ((input.size() & 3) == 3) {
- auto value = table[static_cast<int>(input.back())];
+ auto value = table[static_cast<unsigned char>(input.back())];
if ((value & 3) != 0) {
return false;
}
@@ -246,16 +208,101 @@ bool is_base64url(Slice input) {
return is_base64_impl<true>(input);
}
+template <bool is_url>
+static bool is_base64_characters_impl(Slice input) {
+ auto table = get_character_table<is_url>();
+ for (auto c : input) {
+ if (table[static_cast<unsigned char>(c)] == 64) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool is_base64_characters(Slice input) {
+ return is_base64_characters_impl<false>(input);
+}
+
+bool is_base64url_characters(Slice input) {
+ return is_base64_characters_impl<true>(input);
+}
+
string base64_filter(Slice input) {
+ auto table = get_character_table<false>();
string res;
res.reserve(input.size());
- init_base64_table();
for (auto c : input) {
- if (char_to_value[static_cast<unsigned char>(c)] != 64 || c == '=') {
+ if (table[static_cast<unsigned char>(c)] != 64 || c == '=') {
res += c;
}
}
return res;
}
+static const char *get_base32_characters(bool upper_case) {
+ return upper_case ? "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" : "abcdefghijklmnopqrstuvwxyz234567";
+}
+
+static const unsigned char *get_base32_character_table() {
+ static unsigned char char_to_value[256];
+ static bool is_inited = [] {
+ std::fill(std::begin(char_to_value), std::end(char_to_value), static_cast<unsigned char>(32));
+ auto characters_lc = get_base32_characters(false);
+ auto characters_uc = get_base32_characters(true);
+ for (unsigned char i = 0; i < 32; i++) {
+ char_to_value[static_cast<size_t>(characters_lc[i])] = i;
+ char_to_value[static_cast<size_t>(characters_uc[i])] = i;
+ }
+ return true;
+ }();
+ CHECK(is_inited);
+ return char_to_value;
+}
+
+string base32_encode(Slice input, bool upper_case) {
+ auto *characters = get_base32_characters(upper_case);
+ string base32;
+ base32.reserve((input.size() * 8 + 4) / 5);
+ uint32 c = 0;
+ uint32 length = 0;
+ for (size_t i = 0; i < input.size(); i++) {
+ c = (c << 8) | input.ubegin()[i];
+ length += 8;
+ while (length >= 5) {
+ length -= 5;
+ base32.push_back(characters[(c >> length) & 31]);
+ }
+ }
+ if (length != 0) {
+ base32.push_back(characters[(c << (5 - length)) & 31]);
+ }
+ //TODO: optional padding
+ return base32;
+}
+
+Result<string> base32_decode(Slice base32) {
+ string res;
+ res.reserve(base32.size() * 5 / 8);
+ uint32 c = 0;
+ uint32 length = 0;
+ auto *table = get_base32_character_table();
+ for (size_t i = 0; i < base32.size(); i++) {
+ auto value = table[base32.ubegin()[i]];
+ if (value == 32) {
+ return Status::Error("Wrong character in the string");
+ }
+ c = (c << 5) | value;
+ length += 5;
+ if (length >= 8) {
+ length -= 8;
+ res.push_back(static_cast<char>((c >> length) & 255));
+ }
+ }
+ if ((c & ((1 << length) - 1)) != 0) {
+ return Status::Error("Nonzero padding");
+ }
+ //TODO: check padding
+ return res;
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.h
index cef2b4cb34..4b7449aa65 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/base64.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,7 @@
#pragma once
#include "td/utils/common.h"
+#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
@@ -14,13 +15,21 @@ namespace td {
string base64_encode(Slice input);
Result<string> base64_decode(Slice base64);
+Result<SecureString> base64_decode_secure(Slice base64);
string base64url_encode(Slice input);
Result<string> base64url_decode(Slice base64);
+Result<SecureString> base64url_decode_secure(Slice base64);
bool is_base64(Slice input);
bool is_base64url(Slice input);
+bool is_base64_characters(Slice input);
+bool is_base64url_characters(Slice input);
+
string base64_filter(Slice input);
+string base32_encode(Slice input, bool upper_case = false);
+Result<string> base32_decode(Slice base32);
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/benchmark.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/benchmark.h
index ddc7ad75e6..65a03cb7a3 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/benchmark.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/benchmark.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,14 +15,14 @@
#include <tuple>
#include <utility>
-#define BENCH(name, desc) \
- class name##Bench : public ::td::Benchmark { \
- public: \
- std::string get_description() const override { \
- return (desc); \
- } \
- void run(int n) override; \
- }; \
+#define BENCH(name, desc) \
+ class name##Bench final : public ::td::Benchmark { \
+ public: \
+ std::string get_description() const final { \
+ return (desc); \
+ } \
+ void run(int n) final; \
+ }; \
void name##Bench::run(int n)
namespace td {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/bits.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/bits.h
new file mode 100644
index 0000000000..69bd7ef8c9
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/bits.h
@@ -0,0 +1,310 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#if TD_MSVC
+#include <intrin.h>
+#endif
+
+#ifdef bswap32
+#undef bswap32
+#endif
+
+#ifdef bswap64
+#undef bswap64
+#endif
+
+namespace td {
+
+int32 count_leading_zeroes32(uint32 x);
+int32 count_leading_zeroes64(uint64 x);
+int32 count_trailing_zeroes32(uint32 x);
+int32 count_trailing_zeroes64(uint64 x);
+uint32 bswap32(uint32 x);
+uint64 bswap64(uint64 x);
+int32 count_bits32(uint32 x);
+int32 count_bits64(uint64 x);
+
+inline uint32 bits_negate32(uint32 x) {
+ return ~x + 1;
+}
+
+inline uint64 bits_negate64(uint64 x) {
+ return ~x + 1;
+}
+
+inline uint32 lower_bit32(uint32 x) {
+ return x & bits_negate32(x);
+}
+
+inline uint64 lower_bit64(uint64 x) {
+ return x & bits_negate64(x);
+}
+
+inline uint64 host_to_big_endian64(uint64 x) {
+ // NB: works only for little-endian systems
+ return bswap64(x);
+}
+inline uint64 big_endian_to_host64(uint64 x) {
+ // NB: works only for little-endian systems
+ return bswap64(x);
+}
+
+//TODO: optimize
+inline int32 count_leading_zeroes_non_zero32(uint32 x) {
+ DCHECK(x != 0);
+ return count_leading_zeroes32(x);
+}
+inline int32 count_leading_zeroes_non_zero64(uint64 x) {
+ DCHECK(x != 0);
+ return count_leading_zeroes64(x);
+}
+inline int32 count_trailing_zeroes_non_zero32(uint32 x) {
+ DCHECK(x != 0);
+ return count_trailing_zeroes32(x);
+}
+inline int32 count_trailing_zeroes_non_zero64(uint64 x) {
+ DCHECK(x != 0);
+ return count_trailing_zeroes64(x);
+}
+
+//
+// Platform specific implementation
+//
+#if TD_MSVC
+
+inline int32 count_leading_zeroes32(uint32 x) {
+ unsigned long res = 0;
+ if (_BitScanReverse(&res, x)) {
+ return 31 - res;
+ }
+ return 32;
+}
+
+inline int32 count_leading_zeroes64(uint64 x) {
+#if defined(_M_X64)
+ unsigned long res = 0;
+ if (_BitScanReverse64(&res, x)) {
+ return 63 - res;
+ }
+ return 64;
+#else
+ if ((x >> 32) == 0) {
+ return count_leading_zeroes32(static_cast<uint32>(x)) + 32;
+ } else {
+ return count_leading_zeroes32(static_cast<uint32>(x >> 32));
+ }
+#endif
+}
+
+inline int32 count_trailing_zeroes32(uint32 x) {
+ unsigned long res = 0;
+ if (_BitScanForward(&res, x)) {
+ return res;
+ }
+ return 32;
+}
+
+inline int32 count_trailing_zeroes64(uint64 x) {
+#if defined(_M_X64)
+ unsigned long res = 0;
+ if (_BitScanForward64(&res, x)) {
+ return res;
+ }
+ return 64;
+#else
+ if (static_cast<uint32>(x) == 0) {
+ return count_trailing_zeroes32(static_cast<uint32>(x >> 32)) + 32;
+ } else {
+ return count_trailing_zeroes32(static_cast<uint32>(x));
+ }
+#endif
+}
+
+inline uint32 bswap32(uint32 x) {
+ return _byteswap_ulong(x);
+}
+
+inline uint64 bswap64(uint64 x) {
+ return _byteswap_uint64(x);
+}
+
+inline int32 count_bits32(uint32 x) {
+ // Do not use __popcnt because it will fail on some platforms.
+ x -= (x >> 1) & 0x55555555;
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+ x = (x + (x >> 4)) & 0x0F0F0F0F;
+ x += x >> 8;
+ return (x + (x >> 16)) & 0x3F;
+}
+
+inline int32 count_bits64(uint64 x) {
+#if defined(_M_X64)
+ return static_cast<int32>(__popcnt64(x));
+#else
+ return count_bits32(static_cast<uint32>(x >> 32)) + count_bits32(static_cast<uint32>(x));
+#endif
+}
+
+#elif TD_INTEL
+
+inline int32 count_leading_zeroes32(uint32 x) {
+ unsigned __int32 res = 0;
+ if (_BitScanReverse(&res, x)) {
+ return 31 - res;
+ }
+ return 32;
+}
+
+inline int32 count_leading_zeroes64(uint64 x) {
+#if defined(_M_X64) || defined(__x86_64__)
+ unsigned __int32 res = 0;
+ if (_BitScanReverse64(&res, x)) {
+ return 63 - res;
+ }
+ return 64;
+#else
+ if ((x >> 32) == 0) {
+ return count_leading_zeroes32(static_cast<uint32>(x)) + 32;
+ } else {
+ return count_leading_zeroes32(static_cast<uint32>(x >> 32));
+ }
+#endif
+}
+
+inline int32 count_trailing_zeroes32(uint32 x) {
+ unsigned __int32 res = 0;
+ if (_BitScanForward(&res, x)) {
+ return res;
+ }
+ return 32;
+}
+
+inline int32 count_trailing_zeroes64(uint64 x) {
+#if defined(_M_X64) || defined(__x86_64__)
+ unsigned __int32 res = 0;
+ if (_BitScanForward64(&res, x)) {
+ return res;
+ }
+ return 64;
+#else
+ if (static_cast<uint32>(x) == 0) {
+ return count_trailing_zeroes32(static_cast<uint32>(x >> 32)) + 32;
+ } else {
+ return count_trailing_zeroes32(static_cast<uint32>(x));
+ }
+#endif
+}
+
+inline uint32 bswap32(uint32 x) {
+ return _bswap(static_cast<int>(x));
+}
+
+inline uint64 bswap64(uint64 x) {
+ return _bswap64(static_cast<__int64>(x));
+}
+
+inline int32 count_bits32(uint32 x) {
+ x -= (x >> 1) & 0x55555555;
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+ x = (x + (x >> 4)) & 0x0F0F0F0F;
+ x += x >> 8;
+ return (x + (x >> 16)) & 0x3F;
+}
+
+inline int32 count_bits64(uint64 x) {
+ return count_bits32(static_cast<uint32>(x >> 32)) + count_bits32(static_cast<uint32>(x));
+}
+
+#else
+
+inline int32 count_leading_zeroes32(uint32 x) {
+ if (x == 0) {
+ return 32;
+ }
+ return __builtin_clz(x);
+}
+
+inline int32 count_leading_zeroes64(uint64 x) {
+ if (x == 0) {
+ return 64;
+ }
+ return __builtin_clzll(x);
+}
+
+inline int32 count_trailing_zeroes32(uint32 x) {
+ if (x == 0) {
+ return 32;
+ }
+ return __builtin_ctz(x);
+}
+
+inline int32 count_trailing_zeroes64(uint64 x) {
+ if (x == 0) {
+ return 64;
+ }
+ return __builtin_ctzll(x);
+}
+
+inline uint32 bswap32(uint32 x) {
+ return __builtin_bswap32(x);
+}
+
+inline uint64 bswap64(uint64 x) {
+ return __builtin_bswap64(x);
+}
+
+inline int32 count_bits32(uint32 x) {
+ return __builtin_popcount(x);
+}
+
+inline int32 count_bits64(uint64 x) {
+ return __builtin_popcountll(x);
+}
+
+#endif
+
+struct BitsRange {
+ explicit BitsRange(uint64 bits = 0) : bits{bits}, pos{-1} {
+ }
+
+ BitsRange begin() const {
+ return *this;
+ }
+
+ BitsRange end() const {
+ return BitsRange{};
+ }
+
+ int32 operator*() const {
+ if (pos == -1) {
+ pos = count_trailing_zeroes64(bits);
+ }
+ return pos;
+ }
+
+ bool operator!=(const BitsRange &other) const {
+ return bits != other.bits;
+ }
+
+ BitsRange &operator++() {
+ auto i = **this;
+ if (i != 64) {
+ bits ^= 1ull << i;
+ }
+ pos = -1;
+ return *this;
+ }
+
+ private:
+ uint64 bits{0};
+ mutable int32 pos{-1};
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.cpp
index c1a123031c..f767e23ae4 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.cpp
@@ -1,21 +1,34 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/buffer.h"
+#include "td/utils/logging.h"
#include "td/utils/port/thread_local.h"
+#include <cstddef>
#include <new>
+// fixes https://bugs.llvm.org/show_bug.cgi?id=33723 for clang >= 3.6 + c++11 + libc++
+#if TD_CLANG && _LIBCPP_VERSION
+#define TD_OFFSETOF __builtin_offsetof
+#else
+#define TD_OFFSETOF offsetof
+#endif
+
namespace td {
TD_THREAD_LOCAL BufferAllocator::BufferRawTls *BufferAllocator::buffer_raw_tls; // static zero-initialized
std::atomic<size_t> BufferAllocator::buffer_mem;
+int64 BufferAllocator::get_buffer_slice_size() {
+ return 0;
+}
+
size_t BufferAllocator::get_buffer_mem() {
return buffer_mem;
}
@@ -76,30 +89,113 @@ BufferAllocator::ReaderPtr BufferAllocator::create_reader(const ReaderPtr &raw)
void BufferAllocator::dec_ref_cnt(BufferRaw *ptr) {
int left = ptr->ref_cnt_.fetch_sub(1, std::memory_order_acq_rel);
if (left == 1) {
- auto buf_size = max(sizeof(BufferRaw), offsetof(BufferRaw, data_) + ptr->data_size_);
+ auto buf_size = max(sizeof(BufferRaw), TD_OFFSETOF(BufferRaw, data_) + ptr->data_size_);
buffer_mem -= buf_size;
ptr->~BufferRaw();
delete[] ptr;
}
}
+size_t ChainBufferReader::advance(size_t offset, MutableSlice dest) {
+ LOG_CHECK(offset <= size()) << offset << " " << size() << " " << end_.offset() << " " << begin_.offset() << " "
+ << sync_flag_ << " " << dest.size();
+ return begin_.advance(offset, dest);
+}
+
BufferRaw *BufferAllocator::create_buffer_raw(size_t size) {
size = (size + 7) & -8;
- auto buf_size = offsetof(BufferRaw, data_) + size;
+ auto buf_size = TD_OFFSETOF(BufferRaw, data_) + size;
if (buf_size < sizeof(BufferRaw)) {
buf_size = sizeof(BufferRaw);
}
buffer_mem += buf_size;
auto *buffer_raw = reinterpret_cast<BufferRaw *>(new char[buf_size]);
- new (buffer_raw) BufferRaw();
- buffer_raw->data_size_ = size;
- buffer_raw->begin_ = 0;
- buffer_raw->end_ = 0;
+ return new (buffer_raw) BufferRaw(size);
+}
+
+void BufferBuilder::append(BufferSlice slice) {
+ if (append_inplace(slice.as_slice())) {
+ return;
+ }
+ append_slow(std::move(slice));
+}
+
+void BufferBuilder::append(Slice slice) {
+ if (append_inplace(slice)) {
+ return;
+ }
+ append_slow(BufferSlice(slice));
+}
+
+void BufferBuilder::prepend(BufferSlice slice) {
+ if (prepend_inplace(slice.as_slice())) {
+ return;
+ }
+ prepend_slow(std::move(slice));
+}
+
+void BufferBuilder::prepend(Slice slice) {
+ if (prepend_inplace(slice)) {
+ return;
+ }
+ prepend_slow(BufferSlice(slice));
+}
- buffer_raw->ref_cnt_.store(1, std::memory_order_relaxed);
- buffer_raw->has_writer_.store(true, std::memory_order_relaxed);
- buffer_raw->was_reader_ = false;
- return buffer_raw;
+BufferSlice BufferBuilder::extract() {
+ if (to_append_.empty() && to_prepend_.empty()) {
+ return buffer_writer_.as_buffer_slice();
+ }
+ size_t total_size = size();
+ BufferWriter writer(0, 0, total_size);
+ std::move(*this).for_each([&](auto &&slice) {
+ writer.prepare_append().truncate(slice.size()).copy_from(slice.as_slice());
+ writer.confirm_append(slice.size());
+ });
+ *this = {};
+ return writer.as_buffer_slice();
}
+
+size_t BufferBuilder::size() const {
+ size_t total_size = 0;
+ for_each([&](auto &&slice) { total_size += slice.size(); });
+ return total_size;
+}
+
+bool BufferBuilder::append_inplace(Slice slice) {
+ if (!to_append_.empty()) {
+ return false;
+ }
+ auto dest = buffer_writer_.prepare_append();
+ if (dest.size() < slice.size()) {
+ return false;
+ }
+ dest.remove_suffix(dest.size() - slice.size());
+ dest.copy_from(slice);
+ buffer_writer_.confirm_append(slice.size());
+ return true;
+}
+
+void BufferBuilder::append_slow(BufferSlice slice) {
+ to_append_.push_back(std::move(slice));
+}
+
+bool BufferBuilder::prepend_inplace(Slice slice) {
+ if (!to_prepend_.empty()) {
+ return false;
+ }
+ auto dest = buffer_writer_.prepare_prepend();
+ if (dest.size() < slice.size()) {
+ return false;
+ }
+ dest.remove_prefix(dest.size() - slice.size());
+ dest.copy_from(slice);
+ buffer_writer_.confirm_prepend(slice.size());
+ return true;
+}
+
+void BufferBuilder::prepend_slow(BufferSlice slice) {
+ to_prepend_.push_back(std::move(slice));
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.h
index aa4ef8db26..798ceb9d50 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/buffer.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,33 +7,34 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/Slice.h"
#include <atomic>
-#include <cstring>
#include <limits>
+#include <memory>
namespace td {
struct BufferRaw {
+ explicit BufferRaw(size_t size) : data_size_(size) {
+ }
size_t data_size_;
// Constant after first reader is created.
// May be change by writer before it.
// So writer may do prepends till there is no reader created.
- size_t begin_;
+ size_t begin_ = 0;
// Write by writer.
// Read by reader.
- std::atomic<size_t> end_;
+ std::atomic<size_t> end_{0};
- mutable std::atomic<int32> ref_cnt_;
- std::atomic<bool> has_writer_;
- bool was_reader_;
+ mutable std::atomic<int32> ref_cnt_{1};
+ std::atomic<bool> has_writer_{true};
+ bool was_reader_{false};
- alignas(4) char data_[1];
+ alignas(4) unsigned char data_[1];
};
class BufferAllocator {
@@ -66,10 +67,16 @@ class BufferAllocator {
static ReaderPtr create_reader(const ReaderPtr &raw);
static size_t get_buffer_mem();
+ static int64 get_buffer_slice_size();
static void clear_thread_local();
private:
+ friend class BufferSlice;
+
+ static void track_buffer_slice(int64 size) {
+ }
+
static ReaderPtr create_reader_fast(size_t size);
static WriterPtr create_writer_exact(size_t size);
@@ -103,25 +110,54 @@ class BufferSlice {
return;
}
begin_ = buffer_->begin_;
+ end_ = begin_;
sync_with_writer();
}
BufferSlice(BufferReaderPtr buffer_ptr, size_t begin, size_t end)
: buffer_(std::move(buffer_ptr)), begin_(begin), end_(end) {
+ debug_track();
+ }
+ BufferSlice(const BufferSlice &other) = delete;
+ BufferSlice &operator=(const BufferSlice &other) = delete;
+ BufferSlice(BufferSlice &&other) noexcept : BufferSlice(std::move(other.buffer_), other.begin_, other.end_) {
+ debug_untrack(); // yes, debug_untrack
+ }
+ BufferSlice &operator=(BufferSlice &&other) noexcept {
+ if (this == &other) {
+ return *this;
+ }
+ debug_untrack();
+ buffer_ = std::move(other.buffer_);
+ begin_ = other.begin_;
+ end_ = other.end_;
+ return *this;
}
explicit BufferSlice(size_t size) : buffer_(BufferAllocator::create_reader(size)) {
end_ = buffer_->end_.load(std::memory_order_relaxed);
begin_ = end_ - ((size + 7) & -8);
end_ = begin_ + size;
+ debug_track();
}
explicit BufferSlice(Slice slice) : BufferSlice(slice.size()) {
- std::memcpy(as_slice().begin(), slice.begin(), slice.size());
+ as_slice().copy_from(slice);
}
BufferSlice(const char *ptr, size_t size) : BufferSlice(Slice(ptr, size)) {
}
+ ~BufferSlice() {
+ debug_untrack();
+ }
+
+ void debug_track() const {
+ BufferAllocator::track_buffer_slice(static_cast<int64>(size()));
+ }
+ void debug_untrack() const {
+ BufferAllocator::track_buffer_slice(-static_cast<int64>(size()));
+ }
+
BufferSlice clone() const {
if (is_null()) {
return BufferSlice(BufferReaderPtr(), begin_, end_);
@@ -143,6 +179,10 @@ class BufferSlice {
return Slice(buffer_->data_ + begin_, size());
}
+ operator Slice() const {
+ return as_slice();
+ }
+
MutableSlice as_slice() {
if (is_null()) {
return MutableSlice();
@@ -161,21 +201,27 @@ class BufferSlice {
}
bool confirm_read(size_t size) {
+ debug_untrack();
begin_ += size;
CHECK(begin_ <= end_);
+ debug_track();
return begin_ == end_;
}
void truncate(size_t limit) {
if (size() > limit) {
+ debug_untrack();
end_ = begin_ + limit;
+ debug_track();
}
}
BufferSlice from_slice(Slice slice) const {
auto res = BufferSlice(BufferAllocator::create_reader(buffer_));
- res.begin_ = slice.begin() - buffer_->data_;
- res.end_ = slice.end() - buffer_->data_;
+ res.debug_untrack();
+ res.begin_ = static_cast<size_t>(slice.ubegin() - buffer_->data_);
+ res.end_ = static_cast<size_t>(slice.uend() - buffer_->data_);
+ res.debug_track();
CHECK(buffer_->begin_ <= res.begin_);
CHECK(res.begin_ <= res.end_);
CHECK(res.end_ <= buffer_->end_.load(std::memory_order_relaxed));
@@ -202,20 +248,36 @@ class BufferSlice {
}
size_t size() const {
+ if (is_null()) {
+ return 0;
+ }
return end_ - begin_;
}
+ // like in std::string
+ size_t length() const {
+ return size();
+ }
+
// set end_ into writer's end_
size_t sync_with_writer() {
+ debug_untrack();
CHECK(!is_null());
auto old_end = end_;
end_ = buffer_->end_.load(std::memory_order_acquire);
+ debug_track();
return end_ - old_end;
}
bool is_writer_alive() const {
CHECK(!is_null());
return buffer_->has_writer_.load(std::memory_order_acquire);
}
+ void clear() {
+ debug_untrack();
+ begin_ = 0;
+ end_ = 0;
+ buffer_ = nullptr;
+ }
private:
BufferReaderPtr buffer_;
@@ -241,6 +303,10 @@ class BufferWriter {
BufferWriter(size_t size, size_t prepend, size_t append)
: BufferWriter(BufferAllocator::create_writer(size, prepend, append)) {
}
+ BufferWriter(Slice slice, size_t prepend, size_t append)
+ : BufferWriter(BufferAllocator::create_writer(slice.size(), prepend, append)) {
+ as_slice().copy_from(slice);
+ }
explicit BufferWriter(BufferWriterPtr buffer_ptr) : buffer_(std::move(buffer_ptr)) {
}
@@ -263,6 +329,10 @@ class BufferWriter {
auto end = buffer_->end_.load(std::memory_order_relaxed);
return MutableSlice(buffer_->data_ + buffer_->begin_, buffer_->data_ + end);
}
+ Slice as_slice() const {
+ auto end = buffer_->end_.load(std::memory_order_relaxed);
+ return Slice(buffer_->data_ + buffer_->begin_, buffer_->data_ + end);
+ }
MutableSlice prepare_prepend() {
if (is_null()) {
@@ -481,7 +551,7 @@ class ChainBufferIterator {
// copy to dest if possible
auto to_dest_size = min(ready.size(), dest.size());
if (to_dest_size != 0) {
- std::memcpy(dest.data(), ready.data(), to_dest_size);
+ dest.copy_from(ready.substr(0, to_dest_size));
dest.remove_prefix(to_dest_size);
}
@@ -541,10 +611,7 @@ class ChainBufferReader {
begin_.confirm_read(size);
}
- size_t advance(size_t offset, MutableSlice dest = MutableSlice()) {
- CHECK(offset <= size());
- return begin_.advance(offset, dest);
- }
+ size_t advance(size_t offset, MutableSlice dest = MutableSlice());
size_t size() const {
return end_.offset() - begin_.offset();
@@ -570,14 +637,14 @@ class ChainBufferReader {
// Return [begin_, tail.begin_)
// *this = tail
- ChainBufferReader cut_head(ChainBufferIterator pos) {
+ ChainBufferReader cut_head(ChainBufferIterator pos) TD_WARN_UNUSED_RESULT {
auto tmp = begin_.clone();
begin_ = pos.clone();
return ChainBufferReader(std::move(tmp), std::move(pos), false);
}
- ChainBufferReader cut_head(size_t offset) {
- CHECK(offset <= size()) << offset << " " << size();
+ ChainBufferReader cut_head(size_t offset) TD_WARN_UNUSED_RESULT {
+ CHECK(offset <= size());
auto it = begin_.clone();
it.advance(offset);
return cut_head(std::move(it));
@@ -611,19 +678,10 @@ class ChainBufferReader {
class ChainBufferWriter {
public:
- ChainBufferWriter() {
- init();
- }
-
- // legacy
- static ChainBufferWriter create_empty(size_t size = 0) {
- return ChainBufferWriter();
- }
-
- void init(size_t size = 0) {
- writer_ = BufferWriter(size);
- tail_ = ChainBufferNodeAllocator::create(writer_.as_buffer_slice(), true);
- head_ = ChainBufferNodeAllocator::clone(tail_);
+ ChainBufferWriter()
+ : writer_(0)
+ , tail_(ChainBufferNodeAllocator::create(writer_.as_buffer_slice(), true))
+ , head_(ChainBufferNodeAllocator::clone(tail_)) {
}
MutableSlice prepare_append(size_t hint = 0) {
@@ -634,6 +692,14 @@ class ChainBufferWriter {
}
return res;
}
+ MutableSlice prepare_append_at_least(size_t size) {
+ CHECK(!empty());
+ auto res = prepare_append_inplace();
+ if (res.size() < size) {
+ return prepare_append_alloc(size);
+ }
+ return res;
+ }
MutableSlice prepare_append_inplace() {
CHECK(!empty());
return writer_.prepare_append();
@@ -655,11 +721,11 @@ class ChainBufferWriter {
writer_.confirm_append(size);
}
- void append(Slice slice) {
+ void append(Slice slice, size_t hint = 0) {
while (!slice.empty()) {
- auto ready = prepare_append(slice.size());
+ auto ready = prepare_append(td::max(slice.size(), hint));
auto shift = min(ready.size(), slice.size());
- std::memcpy(ready.data(), slice.data(), shift);
+ ready.copy_from(slice.substr(0, shift));
confirm_append(shift);
slice.remove_prefix(shift);
}
@@ -700,9 +766,73 @@ class ChainBufferWriter {
return !tail_;
}
- ChainBufferNodeReaderPtr head_;
- ChainBufferNodeWriterPtr tail_;
BufferWriter writer_;
+ ChainBufferNodeWriterPtr tail_;
+ ChainBufferNodeReaderPtr head_;
+};
+
+class BufferBuilder {
+ public:
+ BufferBuilder() = default;
+ BufferBuilder(Slice slice, size_t prepend_size, size_t append_size)
+ : buffer_writer_(slice, prepend_size, append_size) {
+ }
+ explicit BufferBuilder(BufferWriter &&buffer_writer) : buffer_writer_(std::move(buffer_writer)) {
+ }
+
+ void append(BufferSlice slice);
+ void append(Slice slice);
+
+ void prepend(BufferSlice slice);
+ void prepend(Slice slice);
+
+ template <class F>
+ void for_each(F &&f) const & {
+ for (auto i = to_prepend_.size(); i > 0; i--) {
+ f(to_prepend_[i - 1].as_slice());
+ }
+ if (!buffer_writer_.empty()) {
+ f(buffer_writer_.as_slice());
+ }
+ for (auto &slice : to_append_) {
+ f(slice.as_slice());
+ }
+ }
+ template <class F>
+ void for_each(F &&f) && {
+ for (auto i = to_prepend_.size(); i > 0; i--) {
+ f(std::move(to_prepend_[i - 1]));
+ }
+ if (!buffer_writer_.empty()) {
+ f(buffer_writer_.as_buffer_slice());
+ }
+ for (auto &slice : to_append_) {
+ f(std::move(slice));
+ }
+ }
+ size_t size() const;
+
+ BufferSlice extract();
+
+ private:
+ BufferWriter buffer_writer_;
+ std::vector<BufferSlice> to_append_;
+ std::vector<BufferSlice> to_prepend_;
+
+ bool append_inplace(Slice slice);
+ void append_slow(BufferSlice slice);
+ bool prepend_inplace(Slice slice);
+ void prepend_slow(BufferSlice slice);
};
+inline Slice as_slice(const BufferSlice &value) {
+ return value.as_slice();
+}
+inline MutableSlice as_slice(BufferSlice &value) {
+ return value.as_slice();
+}
+inline MutableSlice as_mutable_slice(BufferSlice &value) {
+ return value.as_slice();
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/check.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/check.cpp
new file mode 100644
index 0000000000..3651452e52
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/check.cpp
@@ -0,0 +1,23 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/check.h"
+
+#include "td/utils/logging.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+
+namespace td {
+namespace detail {
+
+void process_check_error(const char *message, const char *file, int line) {
+ ::td::Logger(*log_interface, log_options, VERBOSITY_NAME(FATAL), Slice(file), line, Slice())
+ << "Check `" << message << "` failed";
+ ::td::process_fatal_error(PSLICE() << "Check `" << message << "` failed in " << file << " at " << line << '\n');
+}
+
+} // namespace detail
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/check.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/check.h
new file mode 100644
index 0000000000..e6fba40c6f
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/check.h
@@ -0,0 +1,32 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#define TD_DUMMY_CHECK(condition) ((void)(condition))
+
+#define CHECK(condition) \
+ if (!(condition)) { \
+ ::td::detail::process_check_error(#condition, __FILE__, __LINE__); \
+ }
+
+// clang-format off
+#ifdef NDEBUG
+ #define DCHECK TD_DUMMY_CHECK
+#else
+ #define DCHECK CHECK
+#endif
+// clang-format on
+
+#define UNREACHABLE() ::td::detail::process_check_error("Unreachable", __FILE__, __LINE__)
+
+namespace td {
+namespace detail {
+
+[[noreturn]] void process_check_error(const char *message, const char *file, int line);
+
+} // namespace detail
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/common.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/common.h
index d1217016e3..5bf9357f8a 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/common.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/common.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -33,20 +33,29 @@
#define _CRT_SECURE_NO_WARNINGS
#endif
- #include <Winsock2.h>
- #include <ws2tcpip.h>
+ #ifdef __MINGW32__
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+
+ #include <mswsock.h>
+ #include <windows.h>
+ #else
+ #include <WinSock2.h>
+ #include <WS2tcpip.h>
+
+ #include <MSWSock.h>
+ #include <Windows.h>
+ #endif
- #include <Mswsock.h>
- #include <Windows.h>
#undef ERROR
#endif
// clang-format on
+#include "td/utils/check.h"
#include "td/utils/int_types.h"
+#include "td/utils/unique_ptr.h"
-#include <memory>
#include <string>
-#include <utility>
#include <vector>
#define TD_DEBUG
@@ -64,6 +73,13 @@
#endif
// clang-format on
+#if TD_USE_ASAN
+#include <sanitizer/lsan_interface.h>
+#define TD_LSAN_IGNORE(x) __lsan_ignore_object(x)
+#else
+#define TD_LSAN_IGNORE(x) (void)(x)
+#endif
+
namespace td {
inline bool likely(bool x) {
@@ -99,11 +115,6 @@ using string = std::string;
template <class ValueT>
using vector = std::vector<ValueT>;
-template <class ValueT>
-using unique_ptr = std::unique_ptr<ValueT>;
-
-using std::make_unique;
-
struct Unit {};
struct Auto {
@@ -113,14 +124,4 @@ struct Auto {
}
};
-template <class ToT, class FromT>
-ToT &as(FromT *from) {
- return *reinterpret_cast<ToT *>(from);
-}
-
-template <class ToT, class FromT>
-const ToT &as(const FromT *from) {
- return *reinterpret_cast<const ToT *>(from);
-}
-
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h
index ac7462480d..bb346600e8 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h
@@ -1,3 +1,8 @@
#pragma once
+
#define TD_HAVE_OPENSSL 1
#define TD_HAVE_ZLIB 1
+#define TD_HAVE_CRC32C 0
+#define TD_HAVE_COROUTINES 1
+#define TD_HAVE_ABSL 1
+#define TD_FD_DEBUG 1
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h.in b/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h.in
index 92cbd5cdc6..f8b89aeb5c 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h.in
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/config.h.in
@@ -1,3 +1,8 @@
#pragma once
+
#cmakedefine01 TD_HAVE_OPENSSL
#cmakedefine01 TD_HAVE_ZLIB
+#cmakedefine01 TD_HAVE_CRC32C
+#cmakedefine01 TD_HAVE_COROUTINES
+#cmakedefine01 TD_HAVE_ABSL
+#cmakedefine01 TD_FD_DEBUG
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.cpp
index 3e54e673ab..4fcda63e40 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.cpp
@@ -1,103 +1,123 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/crypto.h"
+#include "td/utils/as.h"
#include "td/utils/BigNum.h"
+#include "td/utils/bits.h"
+#include "td/utils/common.h"
+#include "td/utils/Destructor.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/RwMutex.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/Random.h"
+#include "td/utils/ScopeGuard.h"
+#include "td/utils/SharedSlice.h"
+#include "td/utils/StackAllocator.h"
+#include "td/utils/StringBuilder.h"
#if TD_HAVE_OPENSSL
#include <openssl/aes.h>
+#include <openssl/bio.h>
#include <openssl/crypto.h>
+#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/md5.h>
+#include <openssl/opensslv.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
#include <openssl/sha.h>
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#endif
+
#if TD_HAVE_ZLIB
#include <zlib.h>
#endif
+#if TD_HAVE_CRC32C
+#include "crc32c/crc32c.h"
+#endif
+
#include <algorithm>
+#include <cerrno>
#include <cstring>
+#include <mutex>
#include <utility>
namespace td {
-static uint64 gcd(uint64 a, uint64 b) {
+static uint64 pq_gcd(uint64 a, uint64 b) {
if (a == 0) {
return b;
}
- if (b == 0) {
- return a;
- }
-
- int shift = 0;
- while ((a & 1) == 0 && (b & 1) == 0) {
+ while ((a & 1) == 0) {
a >>= 1;
- b >>= 1;
- shift++;
}
+ DCHECK((b & 1) != 0);
while (true) {
- while ((a & 1) == 0) {
- a >>= 1;
- }
- while ((b & 1) == 0) {
- b >>= 1;
- }
if (a > b) {
- a -= b;
+ a = (a - b) >> 1;
+ while ((a & 1) == 0) {
+ a >>= 1;
+ }
} else if (b > a) {
- b -= a;
+ b = (b - a) >> 1;
+ while ((b & 1) == 0) {
+ b >>= 1;
+ }
} else {
- return a << shift;
+ return a;
+ }
+ }
+}
+
+// returns (c + a * b) % pq
+static uint64 pq_add_mul(uint64 c, uint64 a, uint64 b, uint64 pq) {
+ while (b) {
+ if (b & 1) {
+ c += a;
+ if (c >= pq) {
+ c -= pq;
+ }
+ }
+ a += a;
+ if (a >= pq) {
+ a -= pq;
}
+ b >>= 1;
}
+ return c;
}
uint64 pq_factorize(uint64 pq) {
- if (pq < 2 || pq > (static_cast<uint64>(1) << 63)) {
+ if (pq <= 2 || pq > (static_cast<uint64>(1) << 63)) {
return 1;
}
+ if ((pq & 1) == 0) {
+ return 2;
+ }
uint64 g = 0;
- for (int i = 0, it = 0; i < 3 || it < 1000; i++) {
+ for (int i = 0, iter = 0; i < 3 || iter < 1000; i++) {
uint64 q = Random::fast(17, 32) % (pq - 1);
uint64 x = Random::fast_uint64() % (pq - 1) + 1;
uint64 y = x;
int lim = 1 << (min(5, i) + 18);
for (int j = 1; j < lim; j++) {
- it++;
- uint64 a = x;
- uint64 b = x;
- uint64 c = q;
-
- // c += a * b
- while (b) {
- if (b & 1) {
- c += a;
- if (c >= pq) {
- c -= pq;
- }
- }
- a += a;
- if (a >= pq) {
- a -= pq;
- }
- b >>= 1;
- }
-
- x = c;
+ iter++;
+ x = pq_add_mul(q, x, x, pq);
uint64 z = x < y ? pq + x - y : x - y;
- g = gcd(z, pq);
+ g = pq_gcd(z, pq);
if (g != 1) {
break;
}
@@ -123,31 +143,29 @@ uint64 pq_factorize(uint64 pq) {
void init_crypto() {
static bool is_inited = [] {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- return OPENSSL_init_crypto(0, nullptr) != 0;
+ bool result = OPENSSL_init_crypto(0, nullptr) != 0;
#else
OpenSSL_add_all_algorithms();
- return true;
+ bool result = true;
#endif
+ clear_openssl_errors("Init crypto");
+ return result;
}();
CHECK(is_inited);
}
template <class FromT>
static string as_big_endian_string(const FromT &from) {
- size_t size = sizeof(from);
- string res(size, '\0');
-
- auto ptr = reinterpret_cast<const unsigned char *>(&from);
- std::memcpy(&res[0], ptr, size);
+ char res[sizeof(FromT)];
+ as<FromT>(res) = from;
- size_t i = size;
+ size_t i = sizeof(FromT);
while (i && res[i - 1] == 0) {
i--;
}
- res.resize(i);
- std::reverse(res.begin(), res.end());
- return res;
+ std::reverse(res, res + i);
+ return string(res, res + i);
}
static int pq_factorize_big(Slice pq_str, string *p_str, string *q_str) {
@@ -164,14 +182,14 @@ static int pq_factorize_big(Slice pq_str, string *p_str, string *q_str) {
BigNum pq = BigNum::from_binary(pq_str);
bool found = false;
- for (int i = 0, it = 0; !found && (i < 3 || it < 1000); i++) {
+ for (int i = 0, iter = 0; !found && (i < 3 || iter < 1000); i++) {
int32 t = Random::fast(17, 32);
a.set_value(Random::fast_uint32());
b = a;
int32 lim = 1 << (i + 23);
for (int j = 1; j < lim; j++) {
- it++;
+ iter++;
BigNum::mod_mul(a, a, a, pq, context);
a += t;
if (BigNum::compare(a, pq) >= 0) {
@@ -236,156 +254,679 @@ int pq_factorize(Slice pq_str, string *p_str, string *q_str) {
return 0;
}
-/*** AES ***/
-static void aes_ige_xcrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to, bool encrypt_flag) {
- AES_KEY key;
- int err;
- if (encrypt_flag) {
- err = AES_set_encrypt_key(aes_key.raw, 256, &key);
- } else {
- err = AES_set_decrypt_key(aes_key.raw, 256, &key);
+struct AesBlock {
+ uint64 hi;
+ uint64 lo;
+
+ uint8 *raw() {
+ return reinterpret_cast<uint8 *>(this);
+ }
+ const uint8 *raw() const {
+ return reinterpret_cast<const uint8 *>(this);
+ }
+ Slice as_slice() const {
+ return Slice(raw(), AES_BLOCK_SIZE);
}
- LOG_IF(FATAL, err != 0);
- CHECK(from.size() <= to.size());
- AES_ige_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv->raw, encrypt_flag);
-}
-void aes_ige_encrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to) {
- aes_ige_xcrypt(aes_key, aes_iv, from, to, true);
-}
+ AesBlock operator^(const AesBlock &b) const {
+ AesBlock res;
+ res.hi = hi ^ b.hi;
+ res.lo = lo ^ b.lo;
+ return res;
+ }
+ void operator^=(const AesBlock &b) {
+ hi ^= b.hi;
+ lo ^= b.lo;
+ }
-void aes_ige_decrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to) {
- aes_ige_xcrypt(aes_key, aes_iv, from, to, false);
-}
+ void load(const uint8 *from) {
+ *this = as<AesBlock>(from);
+ }
+ void store(uint8 *to) {
+ as<AesBlock>(to) = *this;
+ }
+
+ AesBlock inc() const {
+#if SIZE_MAX == UINT64_MAX
+ AesBlock res;
+ res.lo = host_to_big_endian64(big_endian_to_host64(lo) + 1);
+ if (res.lo == 0) {
+ res.hi = host_to_big_endian64(big_endian_to_host64(hi) + 1);
+ } else {
+ res.hi = hi;
+ }
+ return res;
+#else
+ AesBlock res = *this;
+ auto ptr = res.raw();
+ if (++ptr[15] == 0) {
+ for (int i = 14; i >= 0; i--) {
+ if (++ptr[i] != 0) {
+ break;
+ }
+ }
+ }
+ return res;
+#endif
+ }
+};
+static_assert(sizeof(AesBlock) == 16, "");
+static_assert(sizeof(AesBlock) == AES_BLOCK_SIZE, "");
+
+class Evp {
+ public:
+ Evp() {
+ ctx_ = EVP_CIPHER_CTX_new();
+ LOG_IF(FATAL, ctx_ == nullptr);
+ }
+ Evp(const Evp &from) = delete;
+ Evp &operator=(const Evp &from) = delete;
+ Evp(Evp &&from) = delete;
+ Evp &operator=(Evp &&from) = delete;
+ ~Evp() {
+ CHECK(ctx_ != nullptr);
+ EVP_CIPHER_CTX_free(ctx_);
+ }
+
+ void init_encrypt_ecb(Slice key) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_CIPHER *evp_cipher;
+ if (unlikely(evp_cipher == nullptr)) {
+ init_thread_local_evp_cipher(evp_cipher, "AES-256-ECB");
+ }
+#else
+ const EVP_CIPHER *evp_cipher = EVP_aes_256_ecb();
+#endif
+ init(true, evp_cipher, key);
+ }
+
+ void init_decrypt_ecb(Slice key) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_CIPHER *evp_cipher;
+ if (unlikely(evp_cipher == nullptr)) {
+ init_thread_local_evp_cipher(evp_cipher, "AES-256-ECB");
+ }
+#else
+ const EVP_CIPHER *evp_cipher = EVP_aes_256_ecb();
+#endif
+ init(false, evp_cipher, key);
+ }
+
+ void init_encrypt_cbc(Slice key) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_CIPHER *evp_cipher;
+ if (unlikely(evp_cipher == nullptr)) {
+ init_thread_local_evp_cipher(evp_cipher, "AES-256-CBC");
+ }
+#else
+ const EVP_CIPHER *evp_cipher = EVP_aes_256_cbc();
+#endif
+ init(true, evp_cipher, key);
+ }
+
+ void init_decrypt_cbc(Slice key) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_CIPHER *evp_cipher;
+ if (unlikely(evp_cipher == nullptr)) {
+ init_thread_local_evp_cipher(evp_cipher, "AES-256-CBC");
+ }
+#else
+ const EVP_CIPHER *evp_cipher = EVP_aes_256_cbc();
+#endif
+ init(false, evp_cipher, key);
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ void init_encrypt_ctr(Slice key) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_CIPHER *evp_cipher;
+ if (unlikely(evp_cipher == nullptr)) {
+ init_thread_local_evp_cipher(evp_cipher, "AES-256-CTR");
+ }
+#else
+ const EVP_CIPHER *evp_cipher = EVP_aes_256_ctr();
+#endif
+ init(true, evp_cipher, key);
+ }
+#endif
+
+ void init_iv(Slice iv) {
+ int res = EVP_CipherInit_ex(ctx_, nullptr, nullptr, nullptr, iv.ubegin(), -1);
+ LOG_IF(FATAL, res != 1);
+ }
+
+ void encrypt(const uint8 *src, uint8 *dst, int size) {
+ int len;
+ int res = EVP_EncryptUpdate(ctx_, dst, &len, src, size);
+ LOG_IF(FATAL, res != 1);
+ CHECK(len == size);
+ }
-static void aes_cbc_xcrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to, bool encrypt_flag) {
- AES_KEY key;
- int err;
- if (encrypt_flag) {
- err = AES_set_encrypt_key(aes_key.raw, 256, &key);
+ void decrypt(const uint8 *src, uint8 *dst, int size) {
+ CHECK(size % AES_BLOCK_SIZE == 0);
+ int len;
+ int res = EVP_DecryptUpdate(ctx_, dst, &len, src, size);
+ LOG_IF(FATAL, res != 1);
+ CHECK(len == size);
+ }
+
+ private:
+ EVP_CIPHER_CTX *ctx_{nullptr};
+
+ void init(bool is_encrypt, const EVP_CIPHER *evp_cipher, Slice key) {
+ int res = EVP_CipherInit_ex(ctx_, evp_cipher, nullptr, key.ubegin(), nullptr, is_encrypt ? 1 : 0);
+ LOG_IF(FATAL, res != 1);
+ EVP_CIPHER_CTX_set_padding(ctx_, 0);
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static void init_thread_local_evp_cipher(const EVP_CIPHER *&evp_cipher, const char *algorithm) {
+ evp_cipher = EVP_CIPHER_fetch(nullptr, algorithm, nullptr);
+ LOG_IF(FATAL, evp_cipher == nullptr);
+ detail::add_thread_local_destructor(create_destructor([&evp_cipher]() mutable {
+ EVP_CIPHER_free(const_cast<EVP_CIPHER *>(evp_cipher));
+ evp_cipher = nullptr;
+ }));
+ }
+#endif
+};
+
+struct AesState::Impl {
+ Evp evp;
+};
+
+AesState::AesState() = default;
+AesState::AesState(AesState &&from) noexcept = default;
+AesState &AesState::operator=(AesState &&from) noexcept = default;
+AesState::~AesState() = default;
+
+void AesState::init(Slice key, bool encrypt) {
+ CHECK(key.size() == 32);
+ if (!impl_) {
+ impl_ = make_unique<Impl>();
+ }
+ if (encrypt) {
+ impl_->evp.init_encrypt_ecb(key);
} else {
- err = AES_set_decrypt_key(aes_key.raw, 256, &key);
+ impl_->evp.init_decrypt_ecb(key);
}
- LOG_IF(FATAL, err != 0);
- CHECK(from.size() <= to.size());
- AES_cbc_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv->raw, encrypt_flag);
}
-void aes_cbc_encrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to) {
- aes_cbc_xcrypt(aes_key, aes_iv, from, to, true);
+void AesState::encrypt(const uint8 *src, uint8 *dst, int size) {
+ CHECK(impl_);
+ impl_->evp.encrypt(src, dst, size);
}
-void aes_cbc_decrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to) {
- aes_cbc_xcrypt(aes_key, aes_iv, from, to, false);
+void AesState::decrypt(const uint8 *src, uint8 *dst, int size) {
+ CHECK(impl_);
+ impl_->evp.decrypt(src, dst, size);
}
-class AesCtrState::Impl {
+class AesIgeStateImpl {
public:
- Impl(const UInt256 &key, const UInt128 &iv) {
- if (AES_set_encrypt_key(key.raw, 256, &aes_key) < 0) {
- LOG(FATAL) << "Failed to set encrypt key";
+ void init(Slice key, Slice iv, bool encrypt) {
+ CHECK(key.size() == 32);
+ CHECK(iv.size() == 32);
+ if (encrypt) {
+ evp_.init_encrypt_cbc(key);
+ } else {
+ evp_.init_decrypt_ecb(key);
}
- MutableSlice(counter, AES_BLOCK_SIZE).copy_from({iv.raw, AES_BLOCK_SIZE});
- current_pos = 0;
+
+ encrypted_iv_.load(iv.ubegin());
+ plaintext_iv_.load(iv.ubegin() + AES_BLOCK_SIZE);
+ }
+
+ void get_iv(MutableSlice iv) {
+ CHECK(iv.size() == 32);
+ encrypted_iv_.store(iv.ubegin());
+ plaintext_iv_.store(iv.ubegin() + AES_BLOCK_SIZE);
}
void encrypt(Slice from, MutableSlice to) {
+ CHECK(from.size() % AES_BLOCK_SIZE == 0);
CHECK(to.size() >= from.size());
- for (size_t i = 0; i < from.size(); i++) {
- if (current_pos == 0) {
- AES_encrypt(counter, encrypted_counter, &aes_key);
- for (int j = 15; j >= 0; j--) {
- if (++counter[j] != 0) {
- break;
- }
+ auto len = to.size() / AES_BLOCK_SIZE;
+ auto in = from.ubegin();
+ auto out = to.ubegin();
+
+ static constexpr size_t BLOCK_COUNT = 31;
+ while (len != 0) {
+ AesBlock data[BLOCK_COUNT];
+ AesBlock data_xored[BLOCK_COUNT];
+
+ auto count = td::min(BLOCK_COUNT, len);
+ std::memcpy(data, in, AES_BLOCK_SIZE * count);
+ data_xored[0] = data[0];
+ if (count > 1) {
+ data_xored[1] = plaintext_iv_ ^ data[1];
+ for (size_t i = 2; i < count; i++) {
+ data_xored[i] = data[i - 2] ^ data[i];
}
}
- to[i] = static_cast<char>(from[i] ^ encrypted_counter[current_pos]);
- current_pos = (current_pos + 1) & 15;
+
+ evp_.init_iv(encrypted_iv_.as_slice());
+ auto inlen = static_cast<int>(AES_BLOCK_SIZE * count);
+ evp_.encrypt(data_xored[0].raw(), data_xored[0].raw(), inlen);
+
+ data_xored[0] ^= plaintext_iv_;
+ for (size_t i = 1; i < count; i++) {
+ data_xored[i] ^= data[i - 1];
+ }
+ plaintext_iv_ = data[count - 1];
+ encrypted_iv_ = data_xored[count - 1];
+
+ std::memcpy(out, data_xored, AES_BLOCK_SIZE * count);
+ len -= count;
+ in += AES_BLOCK_SIZE * count;
+ out += AES_BLOCK_SIZE * count;
+ }
+ }
+
+ void decrypt(Slice from, MutableSlice to) {
+ CHECK(from.size() % AES_BLOCK_SIZE == 0);
+ CHECK(to.size() >= from.size());
+ auto len = to.size() / AES_BLOCK_SIZE;
+ auto in = from.ubegin();
+ auto out = to.ubegin();
+
+ AesBlock encrypted;
+
+ while (len) {
+ encrypted.load(in);
+
+ plaintext_iv_ ^= encrypted;
+ evp_.decrypt(plaintext_iv_.raw(), plaintext_iv_.raw(), AES_BLOCK_SIZE);
+ plaintext_iv_ ^= encrypted_iv_;
+
+ plaintext_iv_.store(out);
+ encrypted_iv_ = encrypted;
+
+ --len;
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
}
}
private:
- AES_KEY aes_key;
- uint8 counter[AES_BLOCK_SIZE];
- uint8 encrypted_counter[AES_BLOCK_SIZE];
- uint8 current_pos;
+ Evp evp_;
+ AesBlock encrypted_iv_;
+ AesBlock plaintext_iv_;
+};
+
+AesIgeState::AesIgeState() = default;
+AesIgeState::AesIgeState(AesIgeState &&from) noexcept = default;
+AesIgeState &AesIgeState::operator=(AesIgeState &&from) noexcept = default;
+AesIgeState::~AesIgeState() = default;
+
+void AesIgeState::init(Slice key, Slice iv, bool encrypt) {
+ if (!impl_) {
+ impl_ = make_unique<AesIgeStateImpl>();
+ }
+
+ impl_->init(key, iv, encrypt);
+}
+
+void AesIgeState::encrypt(Slice from, MutableSlice to) {
+ impl_->encrypt(from, to);
+}
+
+void AesIgeState::decrypt(Slice from, MutableSlice to) {
+ impl_->decrypt(from, to);
+}
+
+void aes_ige_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
+ AesIgeStateImpl state;
+ state.init(aes_key, aes_iv, true);
+ state.encrypt(from, to);
+ state.get_iv(aes_iv);
+}
+
+void aes_ige_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
+ AesIgeStateImpl state;
+ state.init(aes_key, aes_iv, false);
+ state.decrypt(from, to);
+ state.get_iv(aes_iv);
+}
+
+void aes_cbc_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
+ CHECK(from.size() <= to.size());
+ CHECK(from.size() % 16 == 0);
+
+ Evp evp;
+ evp.init_encrypt_cbc(aes_key);
+ evp.init_iv(aes_iv);
+ evp.encrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
+ aes_iv.copy_from(to.substr(from.size() - 16));
+}
+
+void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
+ CHECK(from.size() <= to.size());
+ CHECK(from.size() % 16 == 0);
+
+ Evp evp;
+ evp.init_decrypt_cbc(aes_key);
+ evp.init_iv(aes_iv);
+ aes_iv.copy_from(from.substr(from.size() - 16));
+ evp.decrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
+}
+
+struct AesCbcState::Impl {
+ Evp evp_;
+};
+
+AesCbcState::AesCbcState(Slice key256, Slice iv128) : raw_{SecureString(key256), SecureString(iv128)} {
+ CHECK(raw_.key.size() == 32);
+ CHECK(raw_.iv.size() == 16);
+}
+
+AesCbcState::AesCbcState(AesCbcState &&from) noexcept = default;
+AesCbcState &AesCbcState::operator=(AesCbcState &&from) noexcept = default;
+AesCbcState::~AesCbcState() = default;
+
+void AesCbcState::encrypt(Slice from, MutableSlice to) {
+ if (from.empty()) {
+ return;
+ }
+
+ CHECK(from.size() <= to.size());
+ CHECK(from.size() % 16 == 0);
+ if (ctx_ == nullptr) {
+ ctx_ = make_unique<AesCbcState::Impl>();
+ ctx_->evp_.init_encrypt_cbc(raw_.key.as_slice());
+ ctx_->evp_.init_iv(raw_.iv.as_slice());
+ is_encrypt_ = true;
+ } else {
+ CHECK(is_encrypt_);
+ }
+ ctx_->evp_.encrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
+ raw_.iv.as_mutable_slice().copy_from(to.substr(from.size() - 16));
+}
+
+void AesCbcState::decrypt(Slice from, MutableSlice to) {
+ if (from.empty()) {
+ return;
+ }
+
+ CHECK(from.size() <= to.size());
+ CHECK(from.size() % 16 == 0);
+ if (ctx_ == nullptr) {
+ ctx_ = make_unique<AesCbcState::Impl>();
+ ctx_->evp_.init_decrypt_cbc(raw_.key.as_slice());
+ ctx_->evp_.init_iv(raw_.iv.as_slice());
+ is_encrypt_ = false;
+ } else {
+ CHECK(!is_encrypt_);
+ }
+ raw_.iv.as_mutable_slice().copy_from(from.substr(from.size() - 16));
+ ctx_->evp_.decrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
+}
+
+struct AesCtrState::Impl {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ Evp evp_;
+#else
+ AES_KEY aes_key_;
+ uint8 counter_[AES_BLOCK_SIZE];
+ uint8 encrypted_counter_[AES_BLOCK_SIZE];
+ uint8 current_pos_;
+#endif
};
AesCtrState::AesCtrState() = default;
-AesCtrState::AesCtrState(AesCtrState &&from) = default;
-AesCtrState &AesCtrState::operator=(AesCtrState &&from) = default;
+AesCtrState::AesCtrState(AesCtrState &&from) noexcept = default;
+AesCtrState &AesCtrState::operator=(AesCtrState &&from) noexcept = default;
AesCtrState::~AesCtrState() = default;
-void AesCtrState::init(const UInt256 &key, const UInt128 &iv) {
- ctx_ = std::make_unique<AesCtrState::Impl>(key, iv);
+void AesCtrState::init(Slice key, Slice iv) {
+ CHECK(key.size() == 32);
+ CHECK(iv.size() == 16);
+ ctx_ = make_unique<AesCtrState::Impl>();
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ ctx_->evp_.init_encrypt_ctr(key);
+ ctx_->evp_.init_iv(iv);
+#else
+ if (AES_set_encrypt_key(key.ubegin(), 256, &ctx_->aes_key_) < 0) {
+ LOG(FATAL) << "Failed to set encrypt key";
+ }
+ MutableSlice(ctx_->counter_, AES_BLOCK_SIZE).copy_from(iv);
+ ctx_->current_pos_ = 0;
+#endif
}
void AesCtrState::encrypt(Slice from, MutableSlice to) {
- ctx_->encrypt(from, to);
+ CHECK(from.size() <= to.size());
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ ctx_->evp_.encrypt(from.ubegin(), to.ubegin(), narrow_cast<int>(from.size()));
+#else
+ auto from_ptr = from.ubegin();
+ auto to_ptr = to.ubegin();
+ for (size_t i = 0; i < from.size(); i++) {
+ if (ctx_->current_pos_ == 0) {
+ AES_encrypt(ctx_->counter_, ctx_->encrypted_counter_, &ctx_->aes_key_);
+ for (int j = 15; j >= 0; j--) {
+ if (++ctx_->counter_[j] != 0) {
+ break;
+ }
+ }
+ }
+ to_ptr[i] = static_cast<uint8>(from_ptr[i] ^ ctx_->encrypted_counter_[ctx_->current_pos_]);
+ ctx_->current_pos_ = (ctx_->current_pos_ + 1) & 15;
+ }
+#endif
}
void AesCtrState::decrypt(Slice from, MutableSlice to) {
- encrypt(from, to); // it is the same as decrypt
+ encrypt(from, to);
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+static void make_digest(Slice data, MutableSlice output, const EVP_MD *evp_md) {
+ static TD_THREAD_LOCAL EVP_MD_CTX *ctx;
+ if (unlikely(ctx == nullptr)) {
+ ctx = EVP_MD_CTX_new();
+ LOG_IF(FATAL, ctx == nullptr);
+ detail::add_thread_local_destructor(create_destructor([] {
+ EVP_MD_CTX_free(ctx);
+ ctx = nullptr;
+ }));
+ }
+ int res = EVP_DigestInit_ex(ctx, evp_md, nullptr);
+ LOG_IF(FATAL, res != 1);
+ res = EVP_DigestUpdate(ctx, data.ubegin(), data.size());
+ LOG_IF(FATAL, res != 1);
+ res = EVP_DigestFinal_ex(ctx, output.ubegin(), nullptr);
+ LOG_IF(FATAL, res != 1);
+ EVP_MD_CTX_reset(ctx);
+}
+
+static void init_thread_local_evp_md(const EVP_MD *&evp_md, const char *algorithm) {
+ evp_md = EVP_MD_fetch(nullptr, algorithm, nullptr);
+ LOG_IF(FATAL, evp_md == nullptr);
+ detail::add_thread_local_destructor(create_destructor([&evp_md]() mutable {
+ EVP_MD_free(const_cast<EVP_MD *>(evp_md));
+ evp_md = nullptr;
+ }));
+}
+#endif
+
void sha1(Slice data, unsigned char output[20]) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_MD *evp_md;
+ if (unlikely(evp_md == nullptr)) {
+ init_thread_local_evp_md(evp_md, "sha1");
+ }
+ make_digest(data, MutableSlice(output, 20), evp_md);
+#else
auto result = SHA1(data.ubegin(), data.size(), output);
CHECK(result == output);
+#endif
}
void sha256(Slice data, MutableSlice output) {
CHECK(output.size() >= 32);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_MD *evp_md;
+ if (unlikely(evp_md == nullptr)) {
+ init_thread_local_evp_md(evp_md, "sha256");
+ }
+ make_digest(data, output, evp_md);
+#else
auto result = SHA256(data.ubegin(), data.size(), output.ubegin());
CHECK(result == output.ubegin());
+#endif
+}
+
+void sha512(Slice data, MutableSlice output) {
+ CHECK(output.size() >= 64);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_MD *evp_md;
+ if (unlikely(evp_md == nullptr)) {
+ init_thread_local_evp_md(evp_md, "sha512");
+ }
+ make_digest(data, output, evp_md);
+#else
+ auto result = SHA512(data.ubegin(), data.size(), output.ubegin());
+ CHECK(result == output.ubegin());
+#endif
}
-struct Sha256StateImpl {
- SHA256_CTX ctx;
+string sha1(Slice data) {
+ string result(20, '\0');
+ sha1(data, MutableSlice(result).ubegin());
+ return result;
+}
+
+string sha256(Slice data) {
+ string result(32, '\0');
+ sha256(data, result);
+ return result;
+}
+
+string sha512(Slice data) {
+ string result(64, '\0');
+ sha512(data, result);
+ return result;
+}
+
+class Sha256State::Impl {
+ public:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ EVP_MD_CTX *ctx_;
+
+ Impl() {
+ ctx_ = EVP_MD_CTX_new();
+ LOG_IF(FATAL, ctx_ == nullptr);
+ }
+ ~Impl() {
+ CHECK(ctx_ != nullptr);
+ EVP_MD_CTX_free(ctx_);
+ }
+#else
+ SHA256_CTX ctx_;
+ Impl() = default;
+ ~Impl() = default;
+#endif
+
+ Impl(const Impl &from) = delete;
+ Impl &operator=(const Impl &from) = delete;
+ Impl(Impl &&from) = delete;
+ Impl &operator=(Impl &&from) = delete;
};
Sha256State::Sha256State() = default;
-Sha256State::Sha256State(Sha256State &&from) = default;
-Sha256State &Sha256State::operator=(Sha256State &&from) = default;
-Sha256State::~Sha256State() = default;
-void sha256_init(Sha256State *state) {
- state->impl = std::make_unique<Sha256StateImpl>();
- int err = SHA256_Init(&state->impl->ctx);
+Sha256State::Sha256State(Sha256State &&other) noexcept {
+ impl_ = std::move(other.impl_);
+ is_inited_ = other.is_inited_;
+ other.is_inited_ = false;
+}
+
+Sha256State &Sha256State::operator=(Sha256State &&other) noexcept {
+ Sha256State copy(std::move(other));
+ using std::swap;
+ swap(impl_, copy.impl_);
+ swap(is_inited_, copy.is_inited_);
+ return *this;
+}
+
+Sha256State::~Sha256State() {
+ if (is_inited_) {
+ char result[32];
+ extract(MutableSlice{result, 32});
+ CHECK(!is_inited_);
+ }
+}
+
+void Sha256State::init() {
+ if (!impl_) {
+ impl_ = make_unique<Sha256State::Impl>();
+ }
+ CHECK(!is_inited_);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_MD *evp_md;
+ if (unlikely(evp_md == nullptr)) {
+ init_thread_local_evp_md(evp_md, "sha256");
+ }
+ int err = EVP_DigestInit_ex(impl_->ctx_, evp_md, nullptr);
+#else
+ int err = SHA256_Init(&impl_->ctx_);
+#endif
LOG_IF(FATAL, err != 1);
+ is_inited_ = true;
}
-void sha256_update(Slice data, Sha256State *state) {
- CHECK(state->impl);
- int err = SHA256_Update(&state->impl->ctx, data.ubegin(), data.size());
+void Sha256State::feed(Slice data) {
+ CHECK(impl_);
+ CHECK(is_inited_);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ int err = EVP_DigestUpdate(impl_->ctx_, data.ubegin(), data.size());
+#else
+ int err = SHA256_Update(&impl_->ctx_, data.ubegin(), data.size());
+#endif
LOG_IF(FATAL, err != 1);
}
-void sha256_final(Sha256State *state, MutableSlice output) {
+void Sha256State::extract(MutableSlice output, bool destroy) {
CHECK(output.size() >= 32);
- CHECK(state->impl);
- int err = SHA256_Final(output.ubegin(), &state->impl->ctx);
+ CHECK(impl_);
+ CHECK(is_inited_);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ int err = EVP_DigestFinal_ex(impl_->ctx_, output.ubegin(), nullptr);
+#else
+ int err = SHA256_Final(output.ubegin(), &impl_->ctx_);
+#endif
LOG_IF(FATAL, err != 1);
- state->impl.reset();
+ is_inited_ = false;
+ if (destroy) {
+ impl_.reset();
+ }
}
-/*** md5 ***/
void md5(Slice input, MutableSlice output) {
- CHECK(output.size() >= MD5_DIGEST_LENGTH);
+ CHECK(output.size() >= 16);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ static TD_THREAD_LOCAL const EVP_MD *evp_md;
+ if (unlikely(evp_md == nullptr)) {
+ init_thread_local_evp_md(evp_md, "md5");
+ }
+ make_digest(input, output, evp_md);
+#else
auto result = MD5(input.ubegin(), input.size(), output.ubegin());
CHECK(result == output.ubegin());
+#endif
}
-void pbkdf2_sha256(Slice password, Slice salt, int iteration_count, MutableSlice dest) {
- CHECK(dest.size() == 256 / 8) << dest.size();
- CHECK(iteration_count > 0);
- auto evp_md = EVP_sha256();
+static void pbkdf2_impl(Slice password, Slice salt, int iteration_count, MutableSlice dest, const EVP_MD *evp_md) {
CHECK(evp_md != nullptr);
+ int hash_size = EVP_MD_size(evp_md);
+ CHECK(dest.size() == static_cast<size_t>(hash_size));
+ CHECK(iteration_count > 0);
#if OPENSSL_VERSION_NUMBER < 0x10000000L
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
unsigned char counter[4] = {0, 0, 0, 1};
- int password_len = narrow_cast<int>(password.size());
+ auto password_len = narrow_cast<int>(password.size());
HMAC_Init_ex(&ctx, password.data(), password_len, evp_md, nullptr);
HMAC_Update(&ctx, salt.ubegin(), narrow_cast<int>(salt.size()));
HMAC_Update(&ctx, counter, 4);
@@ -393,14 +934,15 @@ void pbkdf2_sha256(Slice password, Slice salt, int iteration_count, MutableSlice
HMAC_CTX_cleanup(&ctx);
if (iteration_count > 1) {
- unsigned char buf[32];
+ CHECK(hash_size <= 64);
+ unsigned char buf[64];
std::copy(dest.ubegin(), dest.uend(), buf);
for (int iter = 1; iter < iteration_count; iter++) {
- if (HMAC(evp_md, password.data(), password_len, buf, 32, buf, nullptr) == nullptr) {
+ if (HMAC(evp_md, password.data(), password_len, buf, hash_size, buf, nullptr) == nullptr) {
LOG(FATAL) << "Failed to HMAC";
}
- for (int i = 0; i < 32; i++) {
- dest[i] ^= buf[i];
+ for (int i = 0; i < hash_size; i++) {
+ dest[i] = static_cast<unsigned char>(dest[i] ^ buf[i]);
}
}
}
@@ -412,14 +954,179 @@ void pbkdf2_sha256(Slice password, Slice salt, int iteration_count, MutableSlice
#endif
}
-void hmac_sha256(Slice key, Slice message, MutableSlice dest) {
- CHECK(dest.size() == 256 / 8);
+void pbkdf2_sha256(Slice password, Slice salt, int iteration_count, MutableSlice dest) {
+ pbkdf2_impl(password, salt, iteration_count, dest, EVP_sha256());
+}
+
+void pbkdf2_sha512(Slice password, Slice salt, int iteration_count, MutableSlice dest) {
+ pbkdf2_impl(password, salt, iteration_count, dest, EVP_sha512());
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+static void hmac_impl(const char *digest, Slice key, Slice message, MutableSlice dest) {
+ EVP_MAC *hmac = EVP_MAC_fetch(nullptr, "HMAC", nullptr);
+ LOG_IF(FATAL, hmac == nullptr);
+
+ EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(hmac);
+ LOG_IF(FATAL, ctx == nullptr);
+
+ OSSL_PARAM params[2];
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, const_cast<char *>(digest), 0);
+ params[1] = OSSL_PARAM_construct_end();
+
+ int res = EVP_MAC_init(ctx, const_cast<unsigned char *>(key.ubegin()), key.size(), params);
+ LOG_IF(FATAL, res != 1);
+ res = EVP_MAC_update(ctx, message.ubegin(), message.size());
+ LOG_IF(FATAL, res != 1);
+ res = EVP_MAC_final(ctx, dest.ubegin(), nullptr, dest.size());
+ LOG_IF(FATAL, res != 1);
+
+ EVP_MAC_CTX_free(ctx);
+ EVP_MAC_free(hmac);
+}
+#else
+static void hmac_impl(const EVP_MD *evp_md, Slice key, Slice message, MutableSlice dest) {
unsigned int len = 0;
- auto result = HMAC(EVP_sha256(), key.ubegin(), narrow_cast<int>(key.size()), message.ubegin(),
+ auto result = HMAC(evp_md, key.ubegin(), narrow_cast<int>(key.size()), message.ubegin(),
narrow_cast<int>(message.size()), dest.ubegin(), &len);
CHECK(result == dest.ubegin());
CHECK(len == dest.size());
}
+#endif
+
+void hmac_sha256(Slice key, Slice message, MutableSlice dest) {
+ CHECK(dest.size() == 256 / 8);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ hmac_impl("SHA256", key, message, dest);
+#else
+ hmac_impl(EVP_sha256(), key, message, dest);
+#endif
+}
+
+void hmac_sha512(Slice key, Slice message, MutableSlice dest) {
+ CHECK(dest.size() == 512 / 8);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined(LIBRESSL_VERSION_NUMBER)
+ hmac_impl("SHA512", key, message, dest);
+#else
+ hmac_impl(EVP_sha512(), key, message, dest);
+#endif
+}
+
+static int get_evp_pkey_type(EVP_PKEY *pkey) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ return EVP_PKEY_type(pkey->type);
+#else
+ return EVP_PKEY_base_id(pkey);
+#endif
+}
+
+Result<BufferSlice> rsa_encrypt_pkcs1_oaep(Slice public_key, Slice data) {
+ BIO *mem_bio = BIO_new_mem_buf(const_cast<void *>(static_cast<const void *>(public_key.data())),
+ narrow_cast<int>(public_key.size()));
+ SCOPE_EXIT {
+ BIO_vfree(mem_bio);
+ };
+
+ EVP_PKEY *pkey = PEM_read_bio_PUBKEY(mem_bio, nullptr, nullptr, nullptr);
+ if (!pkey) {
+ return Status::Error("Cannot read public key");
+ }
+ SCOPE_EXIT {
+ EVP_PKEY_free(pkey);
+ };
+ if (get_evp_pkey_type(pkey) != EVP_PKEY_RSA) {
+ return Status::Error("Wrong key type, expected RSA");
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+ RSA *rsa = pkey->pkey.rsa;
+ int outlen = RSA_size(rsa);
+ BufferSlice res(outlen);
+ if (RSA_public_encrypt(narrow_cast<int>(data.size()), const_cast<unsigned char *>(data.ubegin()),
+ res.as_slice().ubegin(), rsa, RSA_PKCS1_OAEP_PADDING) != outlen) {
+#else
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, nullptr);
+ if (!ctx) {
+ return Status::Error("Cannot create EVP_PKEY_CTX");
+ }
+ SCOPE_EXIT {
+ EVP_PKEY_CTX_free(ctx);
+ };
+
+ if (EVP_PKEY_encrypt_init(ctx) <= 0) {
+ return Status::Error("Cannot init EVP_PKEY_CTX");
+ }
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
+ return Status::Error("Cannot set RSA_PKCS1_OAEP padding in EVP_PKEY_CTX");
+ }
+
+ size_t outlen;
+ if (EVP_PKEY_encrypt(ctx, nullptr, &outlen, data.ubegin(), data.size()) <= 0) {
+ return Status::Error("Cannot calculate encrypted length");
+ }
+ BufferSlice res(outlen);
+ if (EVP_PKEY_encrypt(ctx, res.as_slice().ubegin(), &outlen, data.ubegin(), data.size()) <= 0) {
+#endif
+ return Status::Error("Cannot encrypt");
+ }
+ return std::move(res);
+}
+
+Result<BufferSlice> rsa_decrypt_pkcs1_oaep(Slice private_key, Slice data) {
+ BIO *mem_bio = BIO_new_mem_buf(const_cast<void *>(static_cast<const void *>(private_key.data())),
+ narrow_cast<int>(private_key.size()));
+ SCOPE_EXIT {
+ BIO_vfree(mem_bio);
+ };
+
+ EVP_PKEY *pkey = PEM_read_bio_PrivateKey(mem_bio, nullptr, nullptr, nullptr);
+ if (!pkey) {
+ return Status::Error("Cannot read private key");
+ }
+ SCOPE_EXIT {
+ EVP_PKEY_free(pkey);
+ };
+ if (get_evp_pkey_type(pkey) != EVP_PKEY_RSA) {
+ return Status::Error("Wrong key type, expected RSA");
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+ RSA *rsa = pkey->pkey.rsa;
+ size_t outlen = RSA_size(rsa);
+ BufferSlice res(outlen);
+ auto inlen = RSA_private_decrypt(narrow_cast<int>(data.size()), const_cast<unsigned char *>(data.ubegin()),
+ res.as_slice().ubegin(), rsa, RSA_PKCS1_OAEP_PADDING);
+ if (inlen == -1) {
+ return Status::Error("Cannot decrypt");
+ }
+ res.truncate(inlen);
+#else
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, nullptr);
+ if (!ctx) {
+ return Status::Error("Cannot create EVP_PKEY_CTX");
+ }
+ SCOPE_EXIT {
+ EVP_PKEY_CTX_free(ctx);
+ };
+
+ if (EVP_PKEY_decrypt_init(ctx) <= 0) {
+ return Status::Error("Cannot init EVP_PKEY_CTX");
+ }
+ if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
+ return Status::Error("Cannot set RSA_PKCS1_OAEP padding in EVP_PKEY_CTX");
+ }
+
+ size_t outlen;
+ if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, data.ubegin(), data.size()) <= 0) {
+ return Status::Error("Cannot calculate decrypted length");
+ }
+ BufferSlice res(outlen);
+ if (EVP_PKEY_decrypt(ctx, res.as_slice().ubegin(), &outlen, data.ubegin(), data.size()) <= 0) {
+ return Status::Error("Cannot decrypt");
+ }
+#endif
+ return std::move(res);
+}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
namespace {
@@ -456,6 +1163,8 @@ void openssl_locking_function(int mode, int n, const char *file, int line) {
void init_openssl_threads() {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ static std::mutex init_mutex;
+ std::lock_guard<std::mutex> lock(init_mutex);
if (CRYPTO_get_locking_callback() == nullptr) {
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
CRYPTO_THREADID_set_callback(openssl_threadid_callback);
@@ -464,6 +1173,38 @@ void init_openssl_threads() {
}
#endif
}
+
+Status create_openssl_error(int code, Slice message) {
+ const int max_result_size = 1 << 12;
+ auto result = StackAllocator::alloc(max_result_size);
+ StringBuilder sb(result.as_slice());
+
+ sb << message;
+ while (unsigned long error_code = ERR_get_error()) {
+ char error_buf[1024];
+ ERR_error_string_n(error_code, error_buf, sizeof(error_buf));
+ Slice error(error_buf, std::strlen(error_buf));
+ sb << "{" << error << "}";
+ }
+ LOG_IF(ERROR, sb.is_error()) << "OpenSSL error buffer overflow";
+ LOG(DEBUG) << sb.as_cslice();
+ return Status::Error(code, sb.as_cslice());
+}
+
+void clear_openssl_errors(Slice source) {
+ if (ERR_peek_error() != 0) {
+ auto error = create_openssl_error(0, "Unprocessed OPENSSL_ERROR");
+ if (!ends_with(error.message(), ":def_load:system lib}")) {
+ LOG(ERROR) << source << ": " << error;
+ }
+ }
+#if TD_PORT_WINDOWS
+ WSASetLastError(0);
+#else
+ errno = 0;
+#endif
+}
+
#endif
#if TD_HAVE_ZLIB
@@ -472,6 +1213,68 @@ uint32 crc32(Slice data) {
}
#endif
+#if TD_HAVE_CRC32C
+uint32 crc32c(Slice data) {
+ return crc32c::Crc32c(data.data(), data.size());
+}
+
+uint32 crc32c_extend(uint32 old_crc, Slice data) {
+ return crc32c::Extend(old_crc, data.ubegin(), data.size());
+}
+
+namespace {
+
+uint32 gf32_matrix_times(const uint32 *matrix, uint32 vector) {
+ uint32 sum = 0;
+ while (vector) {
+ if (vector & 1) {
+ sum ^= *matrix;
+ }
+ vector >>= 1;
+ matrix++;
+ }
+ return sum;
+}
+
+void gf32_matrix_square(uint32 *square, const uint32 *matrix) {
+ for (int n = 0; n < 32; n++) {
+ square[n] = gf32_matrix_times(matrix, matrix[n]);
+ }
+}
+
+} // namespace
+
+uint32 crc32c_extend(uint32 old_crc, uint32 data_crc, size_t data_size) {
+ static uint32 power_buf_raw[1024];
+ static const uint32 *power_buf = [&] {
+ auto *buf = power_buf_raw;
+ buf[0] = 0x82F63B78u;
+ for (int n = 0; n < 31; n++) {
+ buf[n + 1] = 1u << n;
+ }
+ for (int n = 1; n < 32; n++) {
+ gf32_matrix_square(buf + (n << 5), buf + ((n - 1) << 5));
+ }
+ return buf;
+ }();
+
+ if (data_size == 0) {
+ return old_crc;
+ }
+
+ const uint32 *p = power_buf + 64;
+ do {
+ p += 32;
+ if (data_size & 1) {
+ old_crc = gf32_matrix_times(p, old_crc);
+ }
+ data_size >>= 1;
+ } while (data_size != 0);
+ return old_crc ^ data_crc;
+}
+
+#endif
+
static const uint64 crc64_table[256] = {
0x0000000000000000, 0xb32e4cbe03a75f6f, 0xf4843657a840a05b, 0x47aa7ae9abe7ff34, 0x7bd0c384ff8f5e33,
0xc8fe8f3afc28015c, 0x8f54f5d357cffe68, 0x3c7ab96d5468a107, 0xf7a18709ff1ebc66, 0x448fcbb7fcb9e309,
@@ -538,4 +1341,34 @@ uint64 crc64(Slice data) {
return crc64_partial(data, static_cast<uint64>(-1)) ^ static_cast<uint64>(-1);
}
+static const uint16 crc16_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad,
+ 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a,
+ 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
+ 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861,
+ 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
+ 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87,
+ 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
+ 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3,
+ 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290,
+ 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e,
+ 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f,
+ 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
+ 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83,
+ 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
+ 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0};
+
+uint16 crc16(Slice data) {
+ uint32 crc = 0;
+ for (auto c : data) {
+ auto t = (static_cast<unsigned char>(c) ^ (crc >> 8)) & 0xff;
+ crc = crc16_table[t] ^ (crc << 8);
+ }
+ return static_cast<uint16>(crc);
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.h
index 23ac694bfb..d4c2558035 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/crypto.h
@@ -1,13 +1,16 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+#include "td/utils/buffer.h"
#include "td/utils/common.h"
+#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
namespace td {
@@ -18,62 +21,164 @@ void init_crypto();
int pq_factorize(Slice pq_str, string *p_str, string *q_str);
-void aes_ige_encrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to);
-void aes_ige_decrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to);
+class AesState {
+ public:
+ AesState();
+ AesState(const AesState &from) = delete;
+ AesState &operator=(const AesState &from) = delete;
+ AesState(AesState &&from) noexcept;
+ AesState &operator=(AesState &&from) noexcept;
+ ~AesState();
+
+ void init(Slice key, bool encrypt);
+
+ void encrypt(const uint8 *src, uint8 *dst, int size);
+
+ void decrypt(const uint8 *src, uint8 *dst, int size);
+
+ private:
+ struct Impl;
+ unique_ptr<Impl> impl_;
+};
+
+void aes_ige_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
+void aes_ige_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
+
+class AesIgeStateImpl;
+
+class AesIgeState {
+ public:
+ AesIgeState();
+ AesIgeState(const AesIgeState &from) = delete;
+ AesIgeState &operator=(const AesIgeState &from) = delete;
+ AesIgeState(AesIgeState &&from) noexcept;
+ AesIgeState &operator=(AesIgeState &&from) noexcept;
+ ~AesIgeState();
+
+ void init(Slice key, Slice iv, bool encrypt);
-void aes_cbc_encrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to);
-void aes_cbc_decrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to);
+ void encrypt(Slice from, MutableSlice to);
+
+ void decrypt(Slice from, MutableSlice to);
+
+ private:
+ unique_ptr<AesIgeStateImpl> impl_;
+};
+
+void aes_cbc_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
+void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
class AesCtrState {
public:
AesCtrState();
AesCtrState(const AesCtrState &from) = delete;
AesCtrState &operator=(const AesCtrState &from) = delete;
- AesCtrState(AesCtrState &&from);
- AesCtrState &operator=(AesCtrState &&from);
+ AesCtrState(AesCtrState &&from) noexcept;
+ AesCtrState &operator=(AesCtrState &&from) noexcept;
~AesCtrState();
- void init(const UInt256 &key, const UInt128 &iv);
+ void init(Slice key, Slice iv);
void encrypt(Slice from, MutableSlice to);
void decrypt(Slice from, MutableSlice to);
private:
- class Impl;
- std::unique_ptr<Impl> ctx_;
+ struct Impl;
+ unique_ptr<Impl> ctx_;
+};
+
+class AesCbcState {
+ public:
+ AesCbcState(Slice key256, Slice iv128);
+ AesCbcState(const AesCbcState &from) = delete;
+ AesCbcState &operator=(const AesCbcState &from) = delete;
+ AesCbcState(AesCbcState &&from) noexcept;
+ AesCbcState &operator=(AesCbcState &&from) noexcept;
+ ~AesCbcState();
+
+ void encrypt(Slice from, MutableSlice to);
+ void decrypt(Slice from, MutableSlice to);
+
+ struct Raw {
+ SecureString key;
+ SecureString iv;
+ };
+ const Raw &raw() const {
+ return raw_;
+ }
+
+ private:
+ struct Impl;
+ unique_ptr<Impl> ctx_;
+
+ Raw raw_;
+ bool is_encrypt_ = false;
};
void sha1(Slice data, unsigned char output[20]);
void sha256(Slice data, MutableSlice output);
-struct Sha256StateImpl;
+void sha512(Slice data, MutableSlice output);
-struct Sha256State {
+string sha1(Slice data) TD_WARN_UNUSED_RESULT;
+
+string sha256(Slice data) TD_WARN_UNUSED_RESULT;
+
+string sha512(Slice data) TD_WARN_UNUSED_RESULT;
+
+class Sha256State {
+ public:
Sha256State();
- Sha256State(Sha256State &&from);
- Sha256State &operator=(Sha256State &&from);
+ Sha256State(const Sha256State &other) = delete;
+ Sha256State &operator=(const Sha256State &other) = delete;
+ Sha256State(Sha256State &&other) noexcept;
+ Sha256State &operator=(Sha256State &&other) noexcept;
~Sha256State();
- std::unique_ptr<Sha256StateImpl> impl;
-};
-void sha256_init(Sha256State *state);
-void sha256_update(Slice data, Sha256State *state);
-void sha256_final(Sha256State *state, MutableSlice output);
+ void init();
+
+ void feed(Slice data);
+
+ void extract(MutableSlice output, bool destroy = false);
+
+ private:
+ class Impl;
+ unique_ptr<Impl> impl_;
+ bool is_inited_ = false;
+};
void md5(Slice input, MutableSlice output);
void pbkdf2_sha256(Slice password, Slice salt, int iteration_count, MutableSlice dest);
+void pbkdf2_sha512(Slice password, Slice salt, int iteration_count, MutableSlice dest);
+
void hmac_sha256(Slice key, Slice message, MutableSlice dest);
+void hmac_sha512(Slice key, Slice message, MutableSlice dest);
+
+// Interface may be improved
+Result<BufferSlice> rsa_encrypt_pkcs1_oaep(Slice public_key, Slice data);
+Result<BufferSlice> rsa_decrypt_pkcs1_oaep(Slice private_key, Slice data);
void init_openssl_threads();
+
+Status create_openssl_error(int code, Slice message);
+
+void clear_openssl_errors(Slice source);
#endif
#if TD_HAVE_ZLIB
uint32 crc32(Slice data);
#endif
+#if TD_HAVE_CRC32C
+uint32 crc32c(Slice data);
+uint32 crc32c_extend(uint32 old_crc, Slice data);
+uint32 crc32c_extend(uint32 old_crc, uint32 new_crc, size_t data_size);
+#endif
+
uint64 crc64(Slice data);
+uint16 crc16(Slice data);
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.cpp
new file mode 100644
index 0000000000..c5f47d8ac9
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.cpp
@@ -0,0 +1,302 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/emoji.h"
+
+#include "td/utils/base64.h"
+#include "td/utils/FlatHashSet.h"
+#include "td/utils/Gzip.h"
+
+namespace td {
+
+bool is_emoji(Slice str) {
+ constexpr size_t MAX_EMOJI_LENGTH = 35;
+ static const FlatHashSet<Slice, SliceHash> emojis = [max_emoji_length = MAX_EMOJI_LENGTH] {
+#if TD_HAVE_ZLIB
+ Slice packed_emojis(
+ "eJyNnety27i2rV8lVfvf-XXul7frVmfZ6cS6kJJskrLj7tUUaYmOI1uObcUEWLUfJecBdledFzgaJIfJiUkyqyqAJ4wxJzE_QABF2fGPs-"
+ "jdj7PVux_TzbFsjyU7lttj2R3L_t2PC3Ms9t2PYHEsy3c_wg_H8vuxfDyWT8dydizjY5kcy_RYZsfiHYt_LPNjKd79iI5foz-P5Rg_"
+ "OsaPjjGjY8zV-2P5x7EcfVfH-Ktj_-rhWI7XXX07lu_HctReHjWXx-tdHq93efPux9XxmldHn6tj3Kuj39VxXFcX7358Pn79fH4ssI_j_"
+ "3y89u0xt9vLdz--HMf1Zfnu7-tf3h-r36bH6mSCykPlo5qjWqCC7uQc1QWqAFWIKjpWH35FVVpfUT2g2qN6RPUN1ROqF1QHVN-P1e8zVLja2S-"
+ "oEOVshOo3VBjV2T9QnaA6RfUB1e-oPqL6hOoM1RgVRn-GPM4Q-ayMjDzOkMcZ8jhDHmfI4wx5nCGPM-RxhtEfZ_"
+ "1YXaK6QvUZ1TWqP1B9QXWHClme7VDdo0K-Z8j3DPmeId-zZ1TI9wz5niHfs1dUOSqDyqIqjtUY6Y-R_hjpj5H-GOmPkf4Y6Y-R_hjpj5H-"
+ "GOmPkf4Y6Y-R_hjpj5H-GOmPkf4Y6Y-R_hjpj5H-GOmPkf4Y6Y-R_hjpj5H-GOmPkf4Y6Y-R_hjpj_"
+ "9E9U9Uf6GKUa1RJahSVDeoNqi2qDJUt6iAbgx0Y6AbA90Y6MZANwa6MdCNgW6MpTIGvzH4jcFvDH5j8BuD3xjoxkA3AboJ0E2AbgJ0E6CbAN0E"
+ "6CZANwG6CdBNgG4CdBOgmwDdBOgmQDcBugnQTYBuAnQTpD9B-hOkP0H6E6Q_QfoTpD9B-hOkP0H6E6Q_QfoTpD9B-hOkP0H6E6Q_QfoTpD9B-"
+ "hOkP0H6E6Q_QfoTpD9B-hOkP0H6E6Q_wfKZgMEEDKZgMAWDKRhMwWAKBlMwmILBFAymYDAFgykYTJHvFPlOke8U-U6R7xT5TpHvFPlOke8U-"
+ "U6R7xT5TpHvFPlOke8U-U6R7xT5TpHvFPlOke8U-"
+ "U6R4BQZTZHRFBlNkdEUGU2R0RQZTZHRDBnNkNEMGc2Q0QwZzZDRDBnNkNEMGc2Q0QwZzTCrM8zqDLM6w6zOkOUMWc6Q5QxZzpDlDC-"
+ "IGV4QM7wgZnhBzPCCmOEFMcMLYoYXxAwviBleEDO8IGZ4QcxAaAZCMxCagdAMhGYgNAOhGQjNQGgGQjMQmoHQDIRmIDQDoRkIzUBohhUxw4qYY"
+ "UXMAGyGFTHDiphhRczAbwZ-M_Cbgd8M_GbgNwM_D-"
+ "g8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8"
+ "UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9Q8UPNAzQM1D9S8khpWnQ90PladD34--Png54OfD34--"
+ "Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--"
+ "Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--Png54OfD34--M3Bbw5-c_Cbg98c_ObgNwe_"
+ "OfjNwW8OfnPwm4PfHPzm4DcHvzn4zcFvDn5z8JuD3xz85uA3B785-M3Bbw5-c_Cbg98c_ObgNwe_"
+ "OfjNwW8OfnPwm4PfHPzm4DcHvzn4zcFvDn5z8JuD3xz85uA3B785-M3Bbw5-c_Cbg98c_ObgNwe_"
+ "OfjNwW8OfnPwm4PfHPzmoLYAtQWoLUBtAWoLUFuA2gLUFqC2ALUFqC1AbQFqC1BbgNoC1BagtgC1BagtQG0BagtQW4DaAtQWoLYAtQWoLUBtAW"
+ "oLUFuA2gLUFqC2ALUFqC1AbQFqC1BbgNoC1BagtgC1BagtQG0BagtQW4DaAtQWoLYAtQWoLUBtAWoLUFuA2gLUFqC2ALUFqC1AbQFqC1BbYNUt"
+ "kf4S6S-R_hLpL5H5EpkvkfkSmS-R-RKZL5H5EpkvkfkSmS-R-RKZL5H5EpkvkfkSmS-R-RKZL5H5EpkvkfkSmS-R-RKZLzG-c1zjHNc4h-"
+ "QCw73AcC8w3Au8SC4w3QGmO8B0B5juANMdYLoDTHeA6Q4w3QGmO8B0B5juAPkGyDdAvgHyDTDdAZIOkHSApAMkHSDpAAMKMKAASQdIOkDSAZIO"
+ "kHSApAMkHSDpAEkHSDpA0gGSDpB0gIwCJB0g6QBJB5juANMdYLoDTHeA6Q4w3QGmO8B0B5juANMdYLoDTHeA6Q4w3QGmO8B0B5juANMdAGcAfg"
+ "H4BeAXgF8AfiH4heAXgl8IfiH4heAXgl8IfiH4heAXgl8IfiH4heAXgl8IfhHiRYgXIV6EeBHiRYgXIV6EeBHiRYgXIV6EeBHiRYgXIV5UxsN8"
+ "RJiPCPMRYT4izEeE-YgwHxHmI8J8RJiPCPMRYT4izEeE-YgwHxHmI8J8RJiPCPMRYT4izEeE-YgwHxHmI8J8RJiPCPMRYT4izEeE-"
+ "YgwHxHmI8J8RJiPCPMRYT4izEeE-YgwHxHmI8J8RJiPCPMRYT4izEeE-YgwHxHmYwV-K_Bbgd8K_FbgtwK_"
+ "FQitgGQFJCsgWQHECiBWALFC0iskvULSKyS9wuhXGP0KQ1thaCsMbYWhrTC0FYa2wtBWGNoKQ7sGq2uwugara7C6BqtrsLoGq2uwugara7C6Bq"
+ "trXO0aXGIMN8aExpjQGBMaY_"
+ "QxRh9j9DEmNMaExsgjRh4x8ogxoTEmNMaExpjQGLnFyC1GbjFyizHIGIOMMcgYg4wxyBiDjDHIGIOMMcgYg4wxyBiDjIEkxoTGmNAYExqXA8eE"
+ "xpjQGBMag1oMajGoxaAWg1oMajGoxQAWYy5jzGWMuVxjLteYyzXmco25XGMu15jLNV4Ga7wM1ngZrPEyWONlsAa1NaitQW0NamtQW4PaGtTWoL"
+ "YGtTWorUFtDWprUFuD2hrU1qC2BrU1qK1BbQ1qa1Bbg9oa1Nagtga1NaitQW0NamtQW4PaGtTWoLYGtTWorUFtDWprUFuD2hrU1qC2BrU1qK1B"
+ "bQ1qa1Bbg9oa1NZYa2ugWwPdGujWQJcAXQJ0CdAlQJcAXQJ0CbaRBPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_"
+ "BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_BPwS8EvALwG_"
+ "BPwS8EvALwG_BPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_"
+ "FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_FPxS8EvBLwW_"
+ "FPw2iLdBvA3ibRBvg3gbxNsg3gbxNoi3QbwN4m0Qb4N4W0zAFhOwxQRsMQFbTMAWE7DFBGwxAVtMwBZ0t6C7Bd0t6G5Bdwu6W9Ddgu4WdLeguw"
+ "XdLehuQXcLulvQ3YLuFnS3oLsF3S3obkF3C7pb0N2C7hZ0t6C7Bd0t6G5Bdwu6W9Ddgu4WNLagsQWNLWhsQWMLGlvQ2ILGFjS2oLEFjS1obEsa"
+ "oLsF2AxcMnDJwCUDlwxcMnDJsOAyLLgMSDIgyYAkA5IMSDIgyYAkA5IMSDIgyYAkA5IM6WdIP0P6GdLPkH6G9DOknyH9DOlnyC1Dbhlyy5Bbht"
+ "wy5JYhtwy5ZS_v_n1z_PcfL9N3_353_Afjxy85Smn--jtKab7_"
+ "J0plHlBK82SBUplLlMo8R6nMC5TKDFAqM0SpzA1KZW5RSvPsBqU0p2WpzC8olXmHUplfUSrzHqUyH1Aq8wWlMg8olfkdpTSP70WPpTTPtyiVma"
+ "FU5hNKaV78glKZryiVmaOU5vHu_Vgq81eUyhyhVOZvKJX5HqUyJyiV6aFUZoBSmVcolfkHSmX-E6Uy_"
+ "0KpzASlMrcolXmHUplfUSrzBaUyDyiV-R2lNI-308dSmSOUyrxGqcw_"
+ "UCrzL5TKXKNUZoJSmTcolfmKUpkWpTSPt8THUpkLlMpcolTmOUplXqBUZohSmSuUyrxEqcw_"
+ "UCozRanMHUpl3qOU5uoDSmWWpTI9lMqco1TmBqUydyiVeY9SmY8olfmMUpkvKJV5QCnNyxFKZX5AqczfUSrzDKUyxyiVWZbK9FEqc4FSmecolX"
+ "mFUpl_olTmHqUyH1FK8-o9SmWeolTmXyiVGaOU5uc_UUrzeGt5LJX5DaU0b_-BUpknKJV5igLz__"
+ "6yQ6lMg1KaHy9QKjNEgfn39T92ZVU37suqbtiyqhtFWVWND6OyqhvPZVU1zv4sq7oRl1XdWJdV3UjKqm6kZVU3bsqqbmzKqm5sy6puZGVVN27L"
+ "qm48lVXVGJuyqhqT87KqGxdlVTfCsqobUVnVjVVZ1Y3PZVU3rsuqakw_"
+ "lVXdOCurujEuq7oxKau6sSirurEsq7pxXlZ146Ks6kZQVnUjLKu6EZVV3ViVVd24LKu6cVVWdeNzWdWN67KqG_uyqhvfyqpuPJdV1ZiVnx_"
+ "Ui8L7tayqxtyUVdVY_"
+ "l5WdeNjWdWNr2VVN3ZlVTf2ZVU3Hsuqbnwrq7rxVFZ147ms6sZLWdWNQ1lVjfPTsqobH8uqbnwqq7pxVlZ1Y1xWdWNWVnVjXVZ146as6sZ9WdW"
+ "Nh7KqG3lZVY2LUVnVjd_Kqm68L6u64ZVV3fDLqm7My6puXJZV3bgqq7rxuazqxp9lVTf-"
+ "Kqu6cVNWdeNrWdWNfVnVje9lVTVWn8qqbozLqm5MyqpuVFXd-KOs6safZVU3_llWdeOvsqobcVnVjXVZ1Y1NWdWNXVnVjX1ZofFvP377692_"
+ "HS18_U9lVTf-c1nVjf9SVnXjv5ZV3fhvZVU3_ntZ1Y3_UVZ143-WVd34X2VVN_53WdWN_"
+ "1NWdePv69PkWG5obGhsaWQ0bmnc0bin8UDjkcYTjWcaLzQONL7TyGkYGniBn6Zo0EhpbGhsaWQ0bml8oXFH4yuNexoPNPY0Hmk80Xim8ULjQOO"
+ "VRk7D0igHf8PB3xDvDQd_wzHfcMw3HPMNxwxjR-OexgONPY1HGt9oPNP4TuOVRk7D0LA0yjFvSHXDgW2IbsPxbDiMDa--ofuWKW-"
+ "Z8pYBtwy4ZaZbDnVLvFvi3XLwGWlkHEbGYWQcRsZhZAx4y2Hcctnckvwtx3PLKbjlwG45sFte9Jbkb3mtW5K_"
+ "JfBbrpbb9tVfaBxofKeR0yjJf2E6X3iJL7zEFwb8wjhfGOeOeO-Y1x3zuuOY7xjwjgHvCOqOY77jJe445jte6ysDfmWcr3T_"
+ "ytx31OzIcEeGOzLc0X3HYeza7s80chqWRrmi7jmV95zKe-Z-z0vck-E9A94znXumc090MF5plNd64CUeGPmBVB-Y4ANXywMzfWCmD7z6A8k_"
+ "MOUHpvxAdA_M_YFT8MAxP3DMDxzzA8f8wDE_EBQMQ-Mti5LYnunsmc6eWeyZxZ5Z7Mlwz8HvOdQ9h7rnCPccz57XemSm33jRb7zWN17rG6_"
+ "1jcS-kdg3XvRbO86exjONFxoHGjmNMvcnXv2ZV39mFs90f-bgn-n-"
+ "Qi8YKY0bGhsaWxq3NL7QuKPxlcaOxj2NBxp7Go80nmm80DjQeKVhaFgaJfkDB3_gmA8c84HkDxzzgWM-"
+ "cKgHDvXAoR441AOHeuBQDxzqgSM8cIQHwjxwYN85sO-8-ndG_s7I35nyd-b1ne6vdH9lXq-"
+ "cglcGfCX5VwZ85eTmzD3nJQwztYxjmUXBaxUcYVGnM338f3_8mh7LP49lfSx3dbtwO_fH8texPHZ1Ph_LbS0q8Oin_GGo6mtefzX1V1t_"
+ "LfAkodKVX_P6q6m_2vprgR8NrXTl17z-auqvtv4K3ada96nWfap1n2rdp1p3VuvOat1ZrTurdWe1blzrxrVuXOvGtW5c_4DX5B_1T3-"
+ "VRk7D0LA0SnIj_qjYiD8uNuKPjI34Y2Mjin-j-DeKf6P4N4p_o_g9xe8pfk_xe4rfU3xK8SnFpxSfUnxK8UeKP1L8keKPFH-k-BPFnyj-"
+ "RPEnij9RfEbxGcVnFJ9RfFaLPaLziM4jOo_oPKLziM4jOo_oPKLziM47ofiE4hOKTyg-oZjoPKLziM4jOo_ovA8Uf6D4A8UfKP5A8e8U_"
+ "07x7xT_TvHvFHNSPE6Kx0nxOCkeJ8XjpHicFI-"
+ "T4nFSPE6Kx0nxOCkeJ8XjpHhvkzKmeEzxmOIxxXyleBOKJxRPKJ5QPKF4SvGU4inFU4qnFM8onlE8o3hG8YzihOKE4oTihOKE4pTilOKU4pTil"
+ "OIbim8ovqH4huIbijcUbyjeULyheENxRnFGcUZxRnFG8S3FtxTfUnxL8S3FXyj-QvEXir9Q_IXiO4rvKL6j-"
+ "I7iO4p3FO8o3lG8o3hH8T3F9xTfU3xP8T3FDxQ_UPxA8QPFDxTvKd5TvKd4T_Ge4keKHyl-pPiR4keKv1H8jeJvFH-j-BvFTxQ_"
+ "UfxE8RPFTxQ_U_xM8TPFzxQ_U_xC8QvFLxS_UPxCcU5xTnFOcU5xXov9X2txaeQ0DA1LoxRzf_a5P_vcn33uzz73Z5_7s8_92ef-7HN_9rk_-"
+ "zxhfZ6wPk9YnyeszxPW52buczP3uZn73Mx9buY-N3Ofm7nPzdznZu5zM_e5I_"
+ "nckXzuSD53JJ87ku9R7FHsUexR7FG8pXhL8ZbiLcXbWrzkEl1yiS65RJdcoksu0SWX6JJLdMkluuQSXXKJLr9T_J3i7xR_p_"
+ "h7LT6f1eLSyGkYGpZGKV5SvKR4SfGS4iXF5xSfU3xO8TnF57U45NoIuTZCro2QayPk2gi5NkKujZBrI-"
+ "TaCLk2Qq6NkGsj5NoIuTZCro2QJ2zIEzbkCRvyhA15woY8YUOesCFP2JAnbMgTNuQJG_KEDXnChjxhQ56wIU_"
+ "YkCdsyBM25Akb8oQNuZ5DrueQ6znkeg65nqO_"
+ "anFp5DQMDUujFHOJRlyiEZdoxCUacYlGXKIRl2jEJRpxiUZcohF30Yi7aMRdNOIuGnEXXf1Si0sjp2FoWBqlmJOy4qSsOCkrTsqKkxJTHFMcUx"
+ "xTHL-"
+ "JyTkm55icY3KOyTkOKA4oDigOKA4oDikOKQ4pDikOKY4ojiiOKI4ojiheUbyieEXxiuIVxZcUX1J8SfElxZcUX1F8RfEVxVcUX1H8meLPFH-m-"
+ "DPFnym-pvia4muKrym-ppg3gTFvAmPeBMa8CYx5ExjvKN5RvKN4R_GOYt7JxLyTiXknE_"
+ "NOJuadTMw7mZh3MjHvZGLeycS8k4l5JxPzTibmnUzMO5mYdzIxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4MxX4Mx"
+ "72Ri3snEvJOJeScT804m5p1MzDuZmHcyMe9kYt7JxAeKDxQfKD5QfKDYUGwoNhQbig3FlmJLsaXYUmxr8ZoJrpngmgmumeCaCSbknJBzQs4JOS"
+ "fknJBzQs4JOSfknJBzQnQJ0SVElxBdQnQJ0SVElxBdQnQJ0SWvFL9S_"
+ "ErxK8WvtTjlaZXytEp5WqU8rVKeVilPq5SnVcrTKuVplfK0SrmLptxFU-6iKXfRlLtoyruvlHdfKe--"
+ "Ut59pbz7Sn2KfYp9in2KfYrnFM8pnlM8p3hO8YLiBcULihcULyjmPVLKe6SU90gp75FS3iOlvEdKeY-U8h4p5T1Synuk9ILiC4ovKL6g-"
+ "IJiHkApD6CUB1DKAyjlAZTyAEp5AKU8gFIeQCkPoJQHUMoDKOUBlPIASnkApTyAUh5AKQ-"
+ "glAdQygMo5QGU8gBKeQClPIBSHkApD6CUB1DKAyjlAZTyAMr4PiXj-5SM71Myvk_J-D4l4yO4jI_gMj6Cy_"
+ "gILuMjuIw3rhlvXDPeuGa8cc1445rtKN5RvKN4R_GOYh5AGQ-"
+ "gjAdQxgMo4wGU8QDKeABlPIAyHkAZD6CMB1DGAyjjAZTxAMp4AGU8gDIeQBkPoIwHUMYDKOPGmHFjzLgxZtwYM26MGTfGjBtjxo0x48aYcWPMu"
+ "D9n3J8z7s8Z9-eM-3PGXTTjLppxF824i2Zvb6Vvfvwy_nEVH-u_61_0rL-DHzpQ3yy_43_qV6u-v-tfJKWtv5W27Y5ureqI2x--4yr9F-u_"
+ "5kaLN73X3PT6d1xz03FNzKCYmL9bz-"
+ "Re3Qnq6Gx68iE3t7PpMUNubmfTY4fc3M6mpxhyczvlQhsgM6RRgj5OQxol6KM2pFGCPoZDGiXoI9qrwTuWnkxll-nvsv1db9fKe9d1PrSulVs-"
+ "5OZ2doy-y83t7Misy83t7M-6e1233TrXrOM9pOlfswNBOnF1rtmBIJ3wOtfsQJBOlJ1rdiCI0Mhl-NrfZfq7bH_X27VM77o2Q-taueVDbm5nx-"
+ "i73NzOjsy63NzO_qy713XbrXPNOt5Dmv41OxCkE1fnmh0I0gmvc80OBOlE2blmB4IITc-61l15f5ft73q7lu1d13ZoXSu3fMjN7eyg3-"
+ "XmdnZk1uXmdvZn3b2u226da9bxHtL0r9mBIJ24OtfsQJBOeJ1rdiBIJ8rONTsQRGh61rXuyvu7TH_"
+ "X27WK3nVdDK1r5ZYPubmdHUPscnM7O3h3ubmd_Vl3r-u2W-eadbyHNP1rdiBIJ67ONTsQpBNe55odCNKJsnPNDgQRmp51rbvy_"
+ "i7T31X9pIWY99Z35Jt5Idt0yTauTLyv3_Sk3OfUdQnV93f9rrjjXXX77XTH--iON9D1tzri9ofvuEr_xbqu-Xffm_"
+ "ZWj95UOt3yITe301kffW5up7t2etzcTmd597m5nTW2ISRdnU1PL5KuzqanF0lXZ9PTi6Srs-npRdLV2b2HdngPabr30J8E6cSl9tCfBOmEp_"
+ "bQnwTpRKn20J8EGQQ7tOSGNErwM7C967At-BnY3lXZFvwMbO8abQt-BlZpOk6p7i7T32X7u3qupajKrp5rKQKy6-1a3Q-jWj3d-3X3w6g-"
+ "N7ezg1Tvw6hONzvk5nZ2EO59GKXcNkNIujo7Jq3LrRvJZghJV2fHZHe5dSPpWgjd-_Xg87kO7yFN_178s-dzQ0H6qA1p-"
+ "vfinz2fGwrSR3RI078X9wTpXXmde_FAkEGwQ8txSNO_Fw8EGQQ7tFR7NR1vOLq7TH-X7e_"
+ "quZaamp79Wnf1XEvk1f2QtdXTvV93P2Ttc3M7O0j1PmTtdLNDbm5nB-Heh6zKbTOEpKuz46XQ5daNZDOEpKuzY7K73LqRdC2E7v168Llzh_"
+ "eQpn8v_tlz56EgfdSGNP178c-eOw8F6SM6pOnfi3uC9K68zr14IMgg2KHlOKTp34sHggyCHVqqvZqe_brngX53l-3v6rmWmpqeW2_"
+ "d1XMtkVf3hwetnu79uvvDgz43t7PjJdT74UGnmx1yczs7CPd-eKDcNkNIujo7ZqbLrRvJZghJV2fHZHe5dSPpWgjd-_Xg5ykd3kOa_r34Z5-"
+ "nDAXpozak6d-Lf_Z5ylCQPqJDmv69uCdI78rr3IsHggyCHVqOQ5r-vXggyCDYoaXaq-nZr3s-qOruMv1dPddSU9OzX-uunmuJvLo_FGv1dO_"
+ "X3R-K9bm5nR04ej8U63SzQ25uZwfh3g_FlNtmCElXZ8fMdLl1I9kMIenq7FjuXW7dSLoWQvd-3feZWB-ZIU3_"
+ "XjwQpBNX5148EKQTXudePBCkE2XnXjwQZBDs0JIb0vTvxQNBBsEOLcchTf9ePBBkEOzQUu3V9OzXuivv7zL9Xba7S01Nz36tu3quRTip1_"
+ "529V9rl78xonnJXyDpft7fqVEC8y8EcTVKYP-FIK5GCYp_IYir-TmMfgqD6ffnPZhwf6aDKerO-m-stdpvy6Jefj1deX-X6e-y_V1vw-"
+ "p4jOn8QlP_vLma_iU2EMTV9C-xgSCupn-JDQTpZDIIo5_CYPr9eQ8m3J_pYIq6s70MxUNcnU_PMtRdpr_L9ne9Davj6YwcTPfTmU5N_"
+ "3Y5EMTV9C-xgSCupn-JDQTpZDIIo5_CYPr9eQ8m3J_pYIq6s70MxbMpnU_PMtRdpr_L9ne9DavjTaccTPebzk5N_xIbCOJq-rfLgSCupn-"
+ "JDQTpZDIIo5_CYPr9eQ8m3J_pYIq6s70MxVtunU_PMtRdpr_L9ne9DavjXloOpvteulPTv8QGgria_iU2EMTV9G-XA0Fczc9h9FMYTL8_78GE-"
+ "zMdTFF3tpeheCeh8-lZhrrL9HfZ_q7m93rL7zW_rOt8z3R8z3Z87y1e3nzvteN7puN7tuN7b_FMRzzTMWbTEc90xLMd8WxHPNsxZtsRr-"
+ "iIV3TEKzriFXLM1e_vln8Lj-Z_VP9Vf9U6b5ntjssPLbPVUf8Z7dou_5I07byxyz_jTDtu2a1fGy7_"
+ "fDHtL43tv7bsVsxF67dty79zWdvlHxSk7Td2-"
+ "WezaLfiJPL3M2s0TZPJvn3n3Gm6ghpV03QEb8je2jW2pu38NmaNr2nHTvtGtmuUTfuLbPuvTtu53iJ12reyXSNu2r5s16ibthO_"
+ "QZ5L5LlCnkvkuUKeS-"
+ "S5Qp47yHMHee4gzx3kuYM8d5DnDvLcQZ47yHMHee4gzx3kuYM8d5DnDvLcQZ47yI1EbhRyI5EbhdxI5EYhNw5y4yA3DnLjIDcOcuMgNw5y4yA3"
+ "DnLjIDcOcuMgNw5y4yA3DnLjIDcOciuRW4XcSuRWIbcSuVXIrYPcOsitg9w6yK2D3DrIrYPcOsitg9w6yK2D3DrIrYPcOsitg9w6yK2DvJDIC4"
+ "W8kMgLhbyQyAuFvHCQFw7ywkFeOMgLB3nhIC8c5IWDvHCQFw7ywkFeOMgLB3nhIC8c5IWDvGgh3zS4NwL1psG8EYg3Dd6NQLtpYd20kG5aODct"
+ "lJsWxk0L4aaFb9NCt2lh27SQbVq4Ni1UmxamTQvRpoVn00KzaWORdxWtJpOVdxWtpiuoUam7Cn7nDZlzV9FqOz9WX-"
+ "Nz7ipa7RvZrlE6dxVN23912s71FqnTvpXtGrFzV9G0a9TOXUWrLX8ut0GeK-"
+ "S5RJ4r5LlEnivkuYM8d5DnDvLcQZ47yHMHee4gzx3kuYM8d5DnDvLcQZ47yHMHee4gzx3kuYPcSORGITcSuVHIjURuFHLjIDcOcuMgNw5y4yA3"
+ "DnLjIDcOcuMgNw5y4yA3DnLjIDcOcuMgNw5y4yC3ErlVyK1EbhVyK5Fbhdw6yK2D3DrIrYPcOsitg9w6yK2D3DrIrYPcOsitg9w6yK2D3DrIrY"
+ "PcOsgLibxQyAuJvFDIC4m8UMgLB3nhIC8c5IWDvHCQFw7ywkFeOMgLB3nhIC8c5IWDvHCQFw7ywkFeOMhbdxXlJ94VbpoVqLp13jLbHSVemq2O"
+ "Gmttl0hp541doqQdt-"
+ "ybxi7x0f7S2P5ry27FLHHRvm3sEhNtv7FLPLRbcRL5mXONxr2raH3n3Gm6ghqVe1fx9p03ZPKuot12PlKu8cm7inb7RrZrlPKuotX2nU-"
+ "zfed6NVp5V9Fq14jlXUWrXaOWdxXttvx8tUGeK-"
+ "S5RJ4r5LlEnivkuYM8d5DnDvLcQZ47yHMHee4gzx3kuYM8d5DnDvLcQZ47yHMHee4gzx3kuYPcSORGITcSuVHIjURuFHLjIDcOcuMgNw5y4yA3"
+ "DnLjIDcOcuMgNw5y4yA3DnLjIDcOcuMgNw5y4yC3ErlVyK1EbhVyK5Fbhdw6yK2D3DrIrYPcOsitg9w6yK2D3DrIrYPcOsitg9w6yK2D3DrIrY"
+ "PcOsgLibxQyAuJvFDIC4m8UMgLB3nhIC8c5IWDvHCQFw7ywkFeOMgLB3nhIC8c5IWDvHCQFw7ywkFeOMjf7irqP-EFQPjT6-0WcLW_"
+ "MZKtpjsX3rnrnQvv3PU2wtu43kZ4G9fbCm_relvhbV3vQngXrnchvAvh3XhWHz02jo0Te-o_"
+ "XDz9rXZqTNExapnsaE2ObCrByGm2BLmMkKsIuYyQqwhGRjAqgpERjIpgZQSrIlgZwaoIhYxQqAiFjFCICO8b7_"
+ "fC833j9V56SPatphKMnGZLkMsIuYqQywi5imBkBKMiGBnBqAhWRrAqgpURrIpQyAiFilDICJL9x8b7o_"
+ "D82Hh9lB6SfaupBCOn2RLkMkKuIuQyQq4iGBnBqAhGRjAqgpURrIpgZQSrIhQyQqEiFDKCZP9Jkmw1lWDkNFuCXEbIVYRcRshVBCMjGBXByAhG"
+ "RbAyglURrIxgVYRCRihUhEJGcEi2vMWe_9Y3En30O5Mz0GoqwchptgS5jJCrCLmMkKsIRkYwKoKREYyKYGUEqyJYGcGqCIWMUKgIhYwgZ-"
+ "Cs5e3OwFnL0ZkB7-7Nj6boGLVMdog5azeVYOQ0W4JcRshVhFxGyFUEIyMYFcHICEZFsDKCVRGsjGBVhEJGKFSEQkYQc-Z9bby_Cs-"
+ "vjddX4bFrPHbCY9d47KSHnK2dmq2dnK2dmq2dnK2dmq2dnK2dmq2dnK2dmq2dnK2dmq2dnK2dmq2dnK2dmq2dnK2dmq2dnK2dmq37xvteeN43X"
+ "vfSQ7K_V-zvJft7xf5esr9X7O8l-3vF_l6yv1fs7yX7e8X-XrK_V-zvJft7xf5esr9X7O8l-3vFft9474XnvvHaSw_Jfq_Y7yX7vWK_l-"
+ "z3iv1est8r9nvJfq_Y7yX7vWK_l-z3iv1est8r9nvJfq_Y7yX7vWL_3Hg_C8_nxutZekj2z4r9s2T_rNg_S_bPiv2zZP-s2D9L9s-K_bNk_"
+ "6zYP0v2z4r9s2T_rNg_S_bPiv2zZP_ssvd_"
+ "ffOmKTpGLZMdgn27qQQjp9kS5DJCriLkMkKuIhgZwagIRkYwKoKVEayKYGUEqyIUMkKhIhQygmQ_arxHwnPUeI2kh2Q_"
+ "UuxHkv1IsR9J9iPFfiTZjxT7kWQ_UuxHkv1IsR9J9iPFfiTZjxT7kWQ_UuxHkv1IsT9pvE-E50njdSI9JPsTxf5Esj9R7E8k-xPF_"
+ "kSyP1HsTyT7E8X-RLI_UexPJPsTxf5Esj9R7E8k-xPF_kSyP1HsTxvvU-F52nidSg_J_lSxP5XsTxX7U8n-VLE_lexPFftTyf5UsT-V7E8V-"
+ "1PJ_lSxP5XsTxX7U8n-VLE_lexPXfbVXzN-i9BuKsHIabYEuYyQqwi5jJCrCEZGMCqCkRGMimBlBKsiWBnBqgiFjFCoCIWM4JBseTvvh-u-"
+ "keir_cJ_"
+ "vPnRFB2jlskOMWftphKMnGZLkMsIuYqQywi5imBkBKMiGBnBqAhWRrAqgpURrIpQyAiFilDICGLOwmbXD8WuHza7fngiPSR7teuHctcP1a4fyl"
+ "0_VLt-KHf9UO36odz1Q7Xrh3LXD9WuH8pdP1S7fih3_VDt-qHc9UO164dy1w_Vrh82u34odv2w2fXDU-kh2atdP5S7fqh2_VDu-"
+ "qHa9UO564dq1w_lrh-qXT-Uu36odv1Q7vqh2vVDueuHatcP5a4fql0_lLt-qHb9sHl6SlN0jFomOyR79eQ6lE-uQ_"
+ "XkOpRPrkP15DqUT65D9eQ6lE-uQ_XkOpRPrkP15DqUT65D9eQ6lE-uQ_XkOpRPrkP15DqUT65D9eQ6HDfeY-E5brzG0kOyHyv2Y8l-"
+ "rNiPJfuxYj-W7MeK_ViyHyv2Y8l-rNiPJfuxYj-W7MeK_ViyHyv2Y8l-rNhPGu-J8Jw0XhPpIdlPFPuJZD9R7CeS_USxn0j2E8V-"
+ "ItlPFPuJZD9R7CeS_USxn0j2E8V-ItlPFPuJZD9x2Ud_"
+ "vXnTFB2jlskOwb7dVIKR02wJchkhVxFyGSFXEYyMYFQEIyMYFcHKCFZFsDKCVREKGaFQEQoZQbJ_bLwfhedj4_UoPST7R8X-UbJ_"
+ "VOwfJftHxf5Rsn9U7B8l-0fF_lGyf1TsHyX7R8X-UbJ_VOwfJftHxf5Rsn9U7Jv3BTRFx6hlskOyV-_"
+ "JWt8ZOc2WIJcRchUhlxFyFcHICEZFMDKCURGsjGBVBCsjWBWhkBEKFaGQEST7p8b7SXg-NV5P0kOyf1LsnyT7J8X-SbJ_"
+ "UuyfJPsnxf5Jsn9S7J8k-yfF_kmyf1LsnyT7J8X-SbJ_"
+ "UuyfJPsnl32cvHnTFB2jlskOwb7dVIKR02wJchkhVxFyGSFXEYyMYFQEIyMYFcHKCFZFsDKCVREKGaFQEQoZQbJv9pxY7Dlxs-fE36SHZK_"
+ "2nFjuObHac2K558Rqz4nlnhOrPSeWe06s9pxY7jmx2nNiuefEas-J5Z4Tqz0nlntOrPacWO45sdpz4ubTq1h8ehU3n17Fz9JDslefXrW-"
+ "M3KaLUEuI-QqQi4j5CqCkRGMimBkBKMiWBnBqghWRrAqQiEjFCpCISNI9i-N94vwfGm8XqSHZP-i2L9I9i-K_Ytk_6LYv0j2L4r9i2T_oti_"
+ "SPYviv2LZP-i2L9I9i-K_Ytk_6LYv0j2L4r9ofE-CM9D43WQHpL9QbE_SPYHxf4g2R8U-4Nkf1DsD5L9QbE_SPYHxf4g2R8U-"
+ "4Nkf1DsD5L9QbE_"
+ "SPYHxb7FQObfyl3m3cpZ5tvKVeZp5GwZNVtGzpZRs2XkbBk1W0bOllGzZeRsGTVbRs6WUbNl5GwZNVtGzpZRs2XkbBk1W0bOllGz1bq-"
+ "vHbruvKaVrK3ir2V7K1ibyV7q9hbyd4q9layt4q9leytYm8le6vYW8neKvZWsreKvZXsrcs-aU6IRJwQSXNCJC_SQ7BP1AmRyBMiUSdEIk-"
+ "IRJ0QiTwhEnVCJPKESNQJkcgTIlEnRCJPiESdEIk8IRJ1QiTyhEjUCZHIEyJRJ0TSnBCJOCGS5oRIDtJDslcnRCJPiESdEIk8IRJ1QiTyhEjUC"
+ "ZHIEyJRJ0QiT4hEnRCJPCESdUIk8oRI1AmRyBMiUSdEIk-"
+ "IRJ0QafPUmaboGLVMdgj27aYSjJxmS5DLCLmKkMsIuYpgZASjIhgZwagIVkawKoKVEayKUMgIhYpQyAiSffPUmaboGLVMdkj26qlz6zsjp9kS5"
+ "DJCriLkMkKuIhgZwagIRkYwKoKVEayKYGUEqyIUMkKhIhQygmQ_bbzFR_J1a9Qy2SHZTxX7qWQ_Veynkv1UsZ9K9lPFfirZTxX7qWQ_"
+ "Veynkv1UsZ9K9lPFfirZTxX7qWQ_VewXjfdCeC4ar4X0kOwXiv1Csl8o9gvJfqHYLyT7hWK_kOwXiv1Csl8o9gvJfqHYLyT7hWK_"
+ "kOwXiv1Csl8o9ueN97nwPG-8zqWHZH-u2J9L9ueK_blkf67Yn0v254r9uWR_rtifS_bniv25ZH-u2J9L9ueK_"
+ "blkf67Yn0v254r9ReN9ITwvGq8L6SHZXyj2F5L9hWJ_IdlfKPYXkv2FYn8h2V8o9heS_YVifyHZXyj2F5L9hWJ_"
+ "IdlfKPYXkv2FYh803oHwDBqvQHpI9oFiH0j2gWIfSPaBYh9I9oFiH0j2gWIfSPaBYh9I9oFiH0j2gWIfSPaBYh9I9oFiHzbeofAMG69Qekj2oW"
+ "IfSvahYh9K9qFiH0r2oWIfSvahYh9K9qFiH0r2oWIfSvahYh9K9qFiH0r2oWIfNd6R8Iwar0h6SPaRYh9J9pFiH0n2kWIfSfaRYh9J9pFiH0n2"
+ "kWIfSfaRYh9J9pFiH0n2kWIfSfaRYr9qvFfCc9V4raSHZL9S7FeS_UqxX0n2K8V-JdmvFPuVZL9S7FeS_UqxX0n2K8V-JdmvFPuVZL9S7FeS_"
+ "Uqxv2y8L4XnZeN1KT0k-0vF_lKyv1TsLyX7S8X-UrK_VOwvJftLxf5Ssr9U7C8l-0vF_lKyv1TsLyX7S8X-UrK_VOyvGu8r4XnVeF1JD8n-"
+ "SrG_kuyvFPsryf5Ksb-S7K8U-yvJ_kqxv5LsrxT7K8n-SrG_kuyvFPsryf5Ksb-S7K8U-8-N92fh-bnx-iw8rhuPa-"
+ "Fx3Xhctz34X9DvWvZ9y35o2c7_EV_7NO17p_3gtJ3_8Lzxzx3_3PHPHX_j-BvH3zj-xvG3jr91_K3jbx3_wvEvHP_C8S9a_"
+ "puW76blt2n5bNp6h_fG4b1xeG8c3huH98bhvXF4bxzeG4f3xuG9cXhvHN4bh_fG4b1xeG8c3huH98bhvXF4byRv_reFu5Z937IfWrbz_"
+ "wrWPpJ3u_3gtJ3_JK_xzx3_3PHPHX_j-BvH3zj-xvG3jr91_K3jbx3_wvEvHP_C8X_jzT8Ys1i_a_2FmKpVmptDu-"
+ "PYOnqXv2EdpaVZ9Rxb1e5U9v19ffah1clm-aOMwR8ts_KZfUDrdgVzWQ3sO-yk-XZ52ly9b5n1Zvhrqb8o_6vSX3m5i5uqN7grmz56g2-"
+ "Vjf9uLHiqRiXsetut_-PO9-_-P_-VKLY");
+ static string all_emojis_str = gzdecode(base64url_decode(packed_emojis).ok()).as_slice().str();
+ constexpr size_t EMOJI_COUNT = 4713;
+#else
+ string all_emojis_str;
+ constexpr size_t EMOJI_COUNT = 0;
+#endif
+ FlatHashSet<Slice, SliceHash> all_emojis;
+ all_emojis.reserve(EMOJI_COUNT);
+ for (size_t i = 0; i < all_emojis_str.size(); i++) {
+ CHECK(all_emojis_str[i] != ' ');
+ CHECK(all_emojis_str[i + 1] != ' ');
+ size_t j = i + 2;
+ while (j < all_emojis_str.size() && all_emojis_str[j] != ' ') {
+ j++;
+ }
+ CHECK(j < all_emojis_str.size());
+ all_emojis.insert(Slice(&all_emojis_str[i], &all_emojis_str[j]));
+ CHECK(j - i <= max_emoji_length);
+ i = j;
+ }
+ CHECK(all_emojis.size() == EMOJI_COUNT);
+ return all_emojis;
+ }();
+ if (str.size() > MAX_EMOJI_LENGTH) {
+ return false;
+ }
+ return emojis.count(str) != 0;
+}
+
+int get_fitzpatrick_modifier(Slice emoji) {
+ if (emoji.size() < 4 || emoji[emoji.size() - 4] != '\xF0' || emoji[emoji.size() - 3] != '\x9F' ||
+ emoji[emoji.size() - 2] != '\x8F') {
+ return 0;
+ }
+ auto c = static_cast<unsigned char>(emoji.back());
+ if (c < 0xBB || c > 0xBF) {
+ return 0;
+ }
+ return (c - 0xBB) + 2;
+}
+
+Slice remove_fitzpatrick_modifier(Slice emoji) {
+ while (get_fitzpatrick_modifier(emoji) != 0) {
+ emoji.remove_suffix(4);
+ }
+ return emoji;
+}
+
+string remove_emoji_modifiers(Slice emoji) {
+ string result = emoji.str();
+ remove_emoji_modifiers_in_place(result);
+ return result;
+}
+
+void remove_emoji_modifiers_in_place(string &emoji) {
+ static const Slice modifiers[] = {u8"\uFE0F" /* variation selector-16 */,
+ u8"\u200D\u2640" /* zero width joiner + female sign */,
+ u8"\u200D\u2642" /* zero width joiner + male sign */,
+ u8"\U0001F3FB" /* emoji modifier fitzpatrick type-1-2 */,
+ u8"\U0001F3FC" /* emoji modifier fitzpatrick type-3 */,
+ u8"\U0001F3FD" /* emoji modifier fitzpatrick type-4 */,
+ u8"\U0001F3FE" /* emoji modifier fitzpatrick type-5 */,
+ u8"\U0001F3FF" /* emoji modifier fitzpatrick type-6 */};
+ size_t j = 0;
+ for (size_t i = 0; i < emoji.size();) {
+ bool is_found = false;
+ for (auto &modifier : modifiers) {
+ auto length = modifier.size();
+ if (i + length <= emoji.size() && Slice(&emoji[i], length) == modifier) {
+ // skip modifier
+ i += length;
+ is_found = true;
+ break;
+ }
+ }
+ if (!is_found) {
+ emoji[j++] = emoji[i++];
+ }
+ }
+ if (j != 0) {
+ emoji.resize(j);
+ }
+}
+
+string remove_emoji_selectors(Slice emoji) {
+ if (!is_emoji(emoji)) {
+ return emoji.str();
+ }
+ string str;
+ for (size_t i = 0; i < emoji.size(); i++) {
+ if (i + 3 <= emoji.size() && emoji[i] == '\xEF' && emoji[i + 1] == '\xB8' && emoji[i + 2] == '\x8F') {
+ // skip \uFE0F
+ i += 2;
+ } else {
+ str += emoji[i];
+ }
+ }
+ CHECK(is_emoji(str));
+ return str;
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.h
new file mode 100644
index 0000000000..8bbc2904b5
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/emoji.h
@@ -0,0 +1,32 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Slice.h"
+
+namespace td {
+
+// checks whether the string is an emoji; variation selectors are ignored
+bool is_emoji(Slice str);
+
+// checks whether emoji ends on a Fitzpatrick modifier and returns it's number or 0
+int get_fitzpatrick_modifier(Slice emoji);
+
+// removes all Fitzpatrick modifier from the end of the string
+Slice remove_fitzpatrick_modifier(Slice emoji);
+
+// removes all emoji modifiers from the string
+string remove_emoji_modifiers(Slice emoji);
+
+// removes all emoji modifiers from the string in-place
+void remove_emoji_modifiers_in_place(string &emoji);
+
+// removes all emoji selectors from the string if it is an emoji
+string remove_emoji_selectors(Slice emoji);
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.cpp
index b22418151c..edb5df8677 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,28 +7,69 @@
#include "td/utils/filesystem.h"
#include "td/utils/buffer.h"
-#include "td/utils/logging.h"
+#include "td/utils/misc.h"
#include "td/utils/PathView.h"
#include "td/utils/port/FileFd.h"
+#include "td/utils/port/path.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include "td/utils/unicode.h"
#include "td/utils/utf8.h"
namespace td {
-Result<BufferSlice> read_file(CSlice path, int64 size) {
+namespace {
+
+template <class T>
+T create_empty(size_t size);
+
+template <>
+string create_empty<string>(size_t size) {
+ return string(size, '\0');
+}
+
+template <>
+BufferSlice create_empty<BufferSlice>(size_t size) {
+ return BufferSlice{size};
+}
+
+template <>
+SecureString create_empty<SecureString>(size_t size) {
+ return SecureString{size};
+}
+
+template <class T>
+Result<T> read_file_impl(CSlice path, int64 size, int64 offset) {
TRY_RESULT(from_file, FileFd::open(path, FileFd::Read));
- if (size == -1) {
- size = from_file.get_size();
+ TRY_RESULT(file_size, from_file.get_size());
+ if (offset < 0 || offset > file_size) {
+ return Status::Error("Failed to read file: invalid offset");
+ }
+ if (size < 0 || size > file_size - offset) {
+ size = file_size - offset;
}
- BufferWriter content{static_cast<size_t>(size), 0, 0};
- TRY_RESULT(got_size, from_file.read(content.as_slice()));
+ auto content = create_empty<T>(narrow_cast<size_t>(size));
+ TRY_RESULT(got_size, from_file.pread(as_mutable_slice(content), offset));
if (got_size != static_cast<size_t>(size)) {
return Status::Error("Failed to read file");
}
from_file.close();
- return content.as_buffer_slice();
+ return std::move(content);
+}
+
+} // namespace
+
+Result<BufferSlice> read_file(CSlice path, int64 size, int64 offset) {
+ return read_file_impl<BufferSlice>(path, size, offset);
+}
+
+Result<string> read_file_str(CSlice path, int64 size, int64 offset) {
+ return read_file_impl<string>(path, size, offset);
+}
+
+Result<SecureString> read_file_secure(CSlice path, int64 size, int64 offset) {
+ return read_file_impl<SecureString>(path, size, offset);
}
// Very straightforward function. Don't expect much of it.
@@ -37,18 +78,28 @@ Status copy_file(CSlice from, CSlice to, int64 size) {
return write_file(to, content.as_slice());
}
-Status write_file(CSlice to, Slice data) {
+Status write_file(CSlice to, Slice data, WriteFileOptions options) {
auto size = data.size();
TRY_RESULT(to_file, FileFd::open(to, FileFd::Truncate | FileFd::Create | FileFd::Write));
+ if (options.need_lock) {
+ TRY_STATUS(to_file.lock(FileFd::LockFlags::Write, to.str(), 10));
+ TRY_STATUS(to_file.truncate_to_current_position(0));
+ }
TRY_RESULT(written, to_file.write(data));
- if (written != static_cast<size_t>(size)) {
+ if (written != size) {
return Status::Error(PSLICE() << "Failed to write file: written " << written << " bytes instead of " << size);
}
+ if (options.need_sync) {
+ TRY_STATUS(to_file.sync());
+ }
+ if (options.need_lock) {
+ to_file.lock(FileFd::LockFlags::Unlock, to.str(), 10).ignore();
+ }
to_file.close();
return Status::OK();
}
-static std::string clean_filename_part(Slice name, int max_length) {
+static string clean_filename_part(Slice name, int max_length) {
auto is_ok = [](uint32 code) {
if (code < 32) {
return false;
@@ -84,6 +135,9 @@ static std::string clean_filename_part(Slice name, int max_length) {
uint32 code;
it = next_utf8_unsafe(it, &code);
if (!is_ok(code)) {
+ if (prepare_search_character(code) == 0) {
+ continue;
+ }
code = ' ';
}
if (new_name.empty() && (code == ' ' || code == '.')) {
@@ -99,14 +153,14 @@ static std::string clean_filename_part(Slice name, int max_length) {
return new_name;
}
-std::string clean_filename(CSlice name) {
+string clean_filename(CSlice name) {
if (!check_utf8(name)) {
return {};
}
PathView path_view(name);
- auto filename = clean_filename_part(path_view.file_stem(), 60);
- auto extension = clean_filename_part(path_view.extension(), 20);
+ auto filename = clean_filename_part(path_view.file_stem(), 64);
+ auto extension = clean_filename_part(path_view.extension(), 16);
if (!extension.empty()) {
if (filename.empty()) {
filename = std::move(extension);
@@ -120,4 +174,18 @@ std::string clean_filename(CSlice name) {
return filename;
}
+Status atomic_write_file(CSlice path, Slice data, CSlice path_tmp) {
+ string path_tmp_buf;
+ if (path_tmp.empty()) {
+ path_tmp_buf = path.str() + ".tmp";
+ path_tmp = path_tmp_buf;
+ }
+
+ WriteFileOptions options;
+ options.need_sync = true;
+ options.need_lock = true;
+ TRY_STATUS(write_file(path_tmp, data, options));
+ return rename(path_tmp, path);
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.h
index 4bb1b17191..b437105819 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/filesystem.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,16 +7,28 @@
#pragma once
#include "td/utils/buffer.h"
+#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
-Result<BufferSlice> read_file(CSlice path, int64 size = -1);
+Result<BufferSlice> read_file(CSlice path, int64 size = -1, int64 offset = 0);
+Result<string> read_file_str(CSlice path, int64 size = -1, int64 offset = 0);
+Result<SecureString> read_file_secure(CSlice path, int64 size = -1, int64 offset = 0);
-Status copy_file(CSlice from, CSlice to, int64 size = -1);
+Status copy_file(CSlice from, CSlice to, int64 size = -1) TD_WARN_UNUSED_RESULT;
-Status write_file(CSlice to, Slice data);
+struct WriteFileOptions {
+ bool need_sync = false;
+ bool need_lock = true;
+};
+Status write_file(CSlice to, Slice data, WriteFileOptions options = {}) TD_WARN_UNUSED_RESULT;
+
+string clean_filename(CSlice name);
+
+// writes data to file and ensures that the file is either fully overriden, or is left intact
+// uses path_tmp to temporary store data, then calls rename
+Status atomic_write_file(CSlice path, Slice data, CSlice path_tmp = {});
-std::string clean_filename(CSlice name);
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.cpp
index 44fc264ab5..d3dd6bcd58 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.cpp
@@ -1,13 +1,11 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/find_boundary.h"
-#include "td/utils/logging.h"
-
#include <cstring>
namespace td {
@@ -26,7 +24,7 @@ bool find_boundary(ChainBufferReader range, Slice boundary, size_t &already_read
auto save_range = range.clone();
char x[MAX_BOUNDARY_LENGTH + 4];
range.advance(boundary.size(), {x, sizeof(x)});
- if (std::memcmp(x, boundary.data(), boundary.size()) == 0) {
+ if (Slice(x, boundary.size()) == boundary) {
return true;
}
@@ -35,7 +33,7 @@ bool find_boundary(ChainBufferReader range, Slice boundary, size_t &already_read
range.advance(1);
already_read++;
} else {
- const char *ptr = static_cast<const char *>(std::memchr(ready.data(), boundary[0], ready.size()));
+ const auto *ptr = static_cast<const char *>(std::memchr(ready.data(), boundary[0], ready.size()));
size_t shift;
if (ptr == nullptr) {
shift = ready.size();
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.h
index 5b424cf23c..a0c6f4ff54 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/find_boundary.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/fixed_vector.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/fixed_vector.h
new file mode 100644
index 0000000000..a4bf0af794
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/fixed_vector.h
@@ -0,0 +1,79 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#include <utility>
+
+namespace td {
+
+template <class T>
+class fixed_vector {
+ public:
+ fixed_vector() = default;
+ explicit fixed_vector(size_t size) : ptr_(new T[size]), size_(size) {
+ }
+ fixed_vector(fixed_vector &&other) noexcept {
+ swap(other);
+ }
+ fixed_vector &operator=(fixed_vector &&other) noexcept {
+ swap(other);
+ return *this;
+ }
+ fixed_vector(const fixed_vector &) = delete;
+ fixed_vector &operator=(const fixed_vector &) = delete;
+ ~fixed_vector() {
+ delete[] ptr_;
+ }
+
+ using iterator = T *;
+ using const_iterator = const T *;
+
+ T &operator[](size_t i) {
+ return ptr_[i];
+ }
+ const T &operator[](size_t i) const {
+ return ptr_[i];
+ }
+
+ T *begin() {
+ return ptr_;
+ }
+ const T *begin() const {
+ return ptr_;
+ }
+ T *end() {
+ return ptr_ + size_;
+ }
+ const T *end() const {
+ return ptr_ + size_;
+ }
+
+ bool empty() const {
+ return size() == 0;
+ }
+ size_t size() const {
+ return size_;
+ }
+
+ void swap(fixed_vector<T> &other) {
+ std::swap(ptr_, other.ptr_);
+ std::swap(size_, other.size_);
+ }
+
+ private:
+ T *ptr_{};
+ size_t size_{0};
+};
+
+template <class T>
+void swap(fixed_vector<T> &lhs, fixed_vector<T> &rhs) {
+ lhs.swap(rhs);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/format.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/format.h
index 745ad0d8a5..0baf9b8be0 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/format.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/format.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,7 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/invoke.h" // for tuple_for_each
+#include "td/utils/invoke.h"
#include "td/utils/Slice.h"
#include "td/utils/StringBuilder.h"
@@ -17,7 +17,7 @@
namespace td {
namespace format {
/*** HexDump ***/
-template <std::size_t size, bool reversed = true>
+template <std::size_t size, bool is_reversed = true>
struct HexDumpSize {
const unsigned char *data;
};
@@ -26,12 +26,12 @@ inline char hex_digit(int x) {
return "0123456789abcdef"[x];
}
-template <std::size_t size, bool reversed>
-StringBuilder &operator<<(StringBuilder &builder, const HexDumpSize<size, reversed> &dump) {
+template <std::size_t size, bool is_reversed>
+StringBuilder &operator<<(StringBuilder &builder, const HexDumpSize<size, is_reversed> &dump) {
const unsigned char *ptr = dump.data;
// TODO: append unsafe
for (std::size_t i = 0; i < size; i++) {
- int xy = ptr[reversed ? size - 1 - i : i];
+ int xy = ptr[is_reversed ? size - 1 - i : i];
int x = xy >> 4;
int y = xy & 15;
builder << hex_digit(x) << hex_digit(y);
@@ -46,21 +46,18 @@ struct HexDumpSlice {
template <std::size_t align>
StringBuilder &operator<<(StringBuilder &builder, const HexDumpSlice<align> &dump) {
- std::size_t size = dump.slice.size();
- const unsigned char *ptr = dump.slice.ubegin();
+ const auto str = dump.slice;
+ const auto size = str.size();
builder << '\n';
- const std::size_t part = size % align;
- if (part) {
- builder << HexDumpSlice<1>{Slice(ptr, part)} << '\n';
+ const std::size_t first_part_size = size % align;
+ if (first_part_size) {
+ builder << HexDumpSlice<1>{str.substr(0, first_part_size)} << '\n';
}
- size -= part;
- ptr += part;
- for (std::size_t i = 0; i < size; i += align) {
- builder << HexDumpSize<align>{ptr};
- ptr += align;
+ for (std::size_t i = first_part_size; i < size; i += align) {
+ builder << HexDumpSize<align>{str.ubegin() + i};
if (((i / align) & 15) == 15 || i + align >= size) {
builder << '\n';
@@ -148,7 +145,7 @@ inline StringBuilder &operator<<(StringBuilder &builder, const Escaped &escaped)
builder << static_cast<char>(c);
} else {
const char *oct = "01234567";
- builder << "\\0" << oct[c >> 6] << oct[(c >> 3) & 7] << oct[c & 7];
+ builder << '\\' << oct[c >> 6] << oct[(c >> 3) & 7] << oct[c & 7];
}
}
return builder;
@@ -176,7 +173,7 @@ inline StringBuilder &operator<<(StringBuilder &logger, Time t) {
while (i + 1 < durations_n && t.seconds_ > 10 * durations[i + 1].value) {
i++;
}
- logger << StringBuilder::FixedDouble(t.seconds_ / durations[i].value, 1) << durations[i].name;
+ logger << StringBuilder::FixedDouble(t.seconds_ / durations[i].value, 1) << Slice(durations[i].name);
return logger;
}
@@ -199,10 +196,10 @@ inline StringBuilder &operator<<(StringBuilder &logger, Size t) {
static constexpr size_t sizes_n = sizeof(sizes) / sizeof(NamedValue);
size_t i = 0;
- while (i + 1 < sizes_n && t.size_ > 10 * sizes[i + 1].value) {
+ while (i + 1 < sizes_n && t.size_ >= 100000 * sizes[i].value) {
i++;
}
- logger << t.size_ / sizes[i].value << sizes[i].name;
+ logger << t.size_ / sizes[i].value << Slice(sizes[i].name);
return logger;
}
@@ -230,6 +227,19 @@ StringBuilder &operator<<(StringBuilder &stream, const Array<ArrayT> &array) {
return stream << Slice("}");
}
+inline StringBuilder &operator<<(StringBuilder &stream, const Array<vector<bool>> &array) {
+ bool first = true;
+ stream << Slice("{");
+ for (bool x : array.ref) {
+ if (!first) {
+ stream << Slice(", ");
+ }
+ stream << x;
+ first = false;
+ }
+ return stream << Slice("}");
+}
+
template <class ArrayT>
Array<ArrayT> as_array(const ArrayT &array) {
return Array<ArrayT>{array};
@@ -291,10 +301,26 @@ StringBuilder &operator<<(StringBuilder &sb, const Concat<T> &concat) {
}
template <class... ArgsT>
-auto concat(const ArgsT &... args) {
+auto concat(const ArgsT &...args) {
return Concat<decltype(std::tie(args...))>{std::tie(args...)};
}
+template <class F>
+struct Lambda {
+ const F &f;
+};
+
+template <class F>
+StringBuilder &operator<<(StringBuilder &sb, const Lambda<F> &f) {
+ f.f(sb);
+ return sb;
+}
+
+template <class LambdaT>
+Lambda<LambdaT> lambda(const LambdaT &lambda) {
+ return Lambda<LambdaT>{lambda};
+}
+
} // namespace format
using format::tag;
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/int_types.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/int_types.h
index 08ff1099c2..25493fa183 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/int_types.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/int_types.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,7 +10,6 @@
#include <cstddef>
#include <cstdint>
-#include <cstring>
namespace td {
@@ -43,23 +42,4 @@ static_assert(static_cast<char>(-256) == 0, "Unexpected cast to char implementat
#pragma warning(pop)
#endif
-template <size_t size>
-struct UInt {
- static_assert(size % 8 == 0, "size should be divisible by 8");
- uint8 raw[size / 8];
-};
-
-template <size_t size>
-inline bool operator==(const UInt<size> &a, const UInt<size> &b) {
- return std::memcmp(a.raw, b.raw, sizeof(a.raw)) == 0;
-}
-
-template <size_t size>
-inline bool operator!=(const UInt<size> &a, const UInt<size> &b) {
- return !(a == b);
-}
-
-using UInt128 = UInt<128>;
-using UInt256 = UInt<256>;
-
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/invoke.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/invoke.h
index e9e56fc2c5..d69a8422e3 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/invoke.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/invoke.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -54,7 +54,7 @@ struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {};
template <class Base, class T, class Derived, class... Args>
auto invoke_impl(T Base::*pmf, Derived &&ref,
- Args &&... args) noexcept(noexcept((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)))
+ Args &&...args) noexcept(noexcept((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)))
-> std::enable_if_t<std::is_function<T>::value && std::is_base_of<Base, std::decay<Derived>>::value,
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...))> {
return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...);
@@ -62,7 +62,7 @@ auto invoke_impl(T Base::*pmf, Derived &&ref,
template <class Base, class T, class RefWrap, class... Args>
auto invoke_impl(T Base::*pmf, RefWrap &&ref,
- Args &&... args) noexcept(noexcept((ref.get().*pmf)(std::forward<Args>(args)...)))
+ Args &&...args) noexcept(noexcept((ref.get().*pmf)(std::forward<Args>(args)...)))
-> std::enable_if_t<std::is_function<T>::value && is_reference_wrapper<std::decay_t<RefWrap>>::value,
decltype((ref.get().*pmf)(std::forward<Args>(args)...))>
@@ -72,7 +72,7 @@ auto invoke_impl(T Base::*pmf, RefWrap &&ref,
template <class Base, class T, class Pointer, class... Args>
auto invoke_impl(T Base::*pmf, Pointer &&ptr,
- Args &&... args) noexcept(noexcept(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)))
+ Args &&...args) noexcept(noexcept(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)))
-> std::enable_if_t<std::is_function<T>::value && !is_reference_wrapper<std::decay_t<Pointer>>::value &&
!std::is_base_of<Base, std::decay_t<Pointer>>::value,
decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...))> {
@@ -102,7 +102,7 @@ auto invoke_impl(T Base::*pmd, Pointer &&ptr) noexcept(noexcept((*std::forward<P
}
template <class F, class... Args>
-auto invoke_impl(F &&f, Args &&... args) noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
+auto invoke_impl(F &&f, Args &&...args) noexcept(noexcept(std::forward<F>(f)(std::forward<Args>(args)...)))
-> std::enable_if_t<!std::is_member_pointer<std::decay_t<F>>::value,
decltype(std::forward<F>(f)(std::forward<Args>(args)...))> {
return std::forward<F>(f)(std::forward<Args>(args)...);
@@ -110,24 +110,24 @@ auto invoke_impl(F &&f, Args &&... args) noexcept(noexcept(std::forward<F>(f)(st
template <class F, class... ArgTypes>
auto invoke(F &&f,
- ArgTypes &&... args) noexcept(noexcept(invoke_impl(std::forward<F>(f), std::forward<ArgTypes>(args)...)))
+ ArgTypes &&...args) noexcept(noexcept(invoke_impl(std::forward<F>(f), std::forward<ArgTypes>(args)...)))
-> decltype(invoke_impl(std::forward<F>(f), std::forward<ArgTypes>(args)...)) {
return invoke_impl(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
template <class F, class... Args, std::size_t... S>
-void call_tuple_impl(F &func, std::tuple<Args...> &&tuple, IntSeq<S...>) {
- func(std::forward<Args>(std::get<S>(tuple))...);
+auto call_tuple_impl(F &&func, std::tuple<Args...> &&tuple, IntSeq<S...>) {
+ return func(std::forward<Args>(std::get<S>(tuple))...);
}
template <class... Args, std::size_t... S>
-void invoke_tuple_impl(std::tuple<Args...> &&tuple, IntSeq<S...>) {
- invoke(std::forward<Args>(std::get<S>(tuple))...);
+auto invoke_tuple_impl(std::tuple<Args...> &&tuple, IntSeq<S...>) {
+ return invoke(std::forward<Args>(std::get<S>(tuple))...);
}
-template <class Actor, class F, class... Args, std::size_t... S>
-void mem_call_tuple_impl(Actor *actor, F &func, std::tuple<Args...> &&tuple, IntSeq<S...>) {
- (actor->*func)(std::forward<Args>(std::get<S>(tuple))...);
+template <class ActorT, class F, class... Args, std::size_t... S>
+auto mem_call_tuple_impl(ActorT *actor, std::tuple<F, Args...> &&tuple, IntSeq<0, S...>) {
+ return (actor->*std::get<0>(tuple))(std::forward<Args>(std::get<S>(tuple))...);
}
template <class F, class... Args, std::size_t... S>
@@ -151,18 +151,18 @@ class LogicAnd {
};
template <class F, class... Args>
-void call_tuple(F &func, std::tuple<Args...> &&tuple) {
- detail::call_tuple_impl(func, std::move(tuple), detail::IntRange<sizeof...(Args)>());
+auto call_tuple(F &&func, std::tuple<Args...> &&tuple) {
+ return detail::call_tuple_impl(func, std::move(tuple), detail::IntRange<sizeof...(Args)>());
}
template <class... Args>
-void invoke_tuple(std::tuple<Args...> &&tuple) {
- detail::invoke_tuple_impl(std::move(tuple), detail::IntRange<sizeof...(Args)>());
+auto invoke_tuple(std::tuple<Args...> &&tuple) {
+ return detail::invoke_tuple_impl(std::move(tuple), detail::IntRange<sizeof...(Args)>());
}
-template <class Actor, class F, class... Args>
-void mem_call_tuple(Actor *actor, F &func, std::tuple<Args...> &&tuple) {
- detail::mem_call_tuple_impl(actor, func, std::move(tuple), detail::IntRange<sizeof...(Args)>());
+template <class ActorT, class... Args>
+auto mem_call_tuple(ActorT *actor, std::tuple<Args...> &&tuple) {
+ return detail::mem_call_tuple_impl(actor, std::move(tuple), detail::IntRange<sizeof...(Args)>());
}
template <class F, class... Args>
@@ -175,4 +175,36 @@ void tuple_for_each(const std::tuple<Args...> &tuple, const F &func) {
detail::tuple_for_each_impl(tuple, func, detail::IntRange<sizeof...(Args)>());
}
+template <size_t N, class Arg, class... Args, std::enable_if_t<N == 0, int> = 0>
+auto &&get_nth_argument(Arg &&arg, Args &&...args) {
+ return std::forward<Arg>(arg);
+}
+
+template <size_t N, class Arg, class... Args, std::enable_if_t<N != 0, int> = 0>
+auto &&get_nth_argument(Arg &&arg, Args &&...args) {
+ return get_nth_argument<N - 1>(std::forward<Args &&>(args)...);
+}
+
+template <class... Args>
+auto &&get_last_argument(Args &&...args) {
+ return get_nth_argument<sizeof...(Args) - 1>(std::forward<Args &&>(args)...);
+}
+
+namespace detail {
+template <class F, class... Args, std::size_t... S>
+auto call_n_arguments_impl(IntSeq<S...>, F &&f, Args &&...args) {
+ return f(get_nth_argument<S>(std::forward<Args>(args)...)...);
+}
+} // namespace detail
+
+template <size_t N, class F, class... Args>
+auto call_n_arguments(F &&f, Args &&...args) {
+ return detail::call_n_arguments_impl(detail::IntRange<N>(), f, std::forward<Args>(args)...);
+}
+
+template <class F, class X, class = void>
+struct is_callable final : public std::false_type {};
+template <class F, class X>
+struct is_callable<F, X, decltype(std::declval<F>()(std::declval<X>()))> final : public std::true_type {};
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.cpp
index 17403ff87b..563575bdba 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.cpp
@@ -1,19 +1,21 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/logging.h"
+#include "td/utils/ExitGuard.h"
#include "td/utils/port/Clocks.h"
-#include "td/utils/port/Fd.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/Slice.h"
-#include "td/utils/Time.h"
+#include "td/utils/TsCerr.h"
#include <atomic>
#include <cstdlib>
+#include <limits>
+#include <mutex>
#if TD_ANDROID
#include <android/log.h>
@@ -27,115 +29,129 @@
namespace td {
-int VERBOSITY_NAME(level) = VERBOSITY_NAME(DEBUG) + 1;
-int VERBOSITY_NAME(net_query) = VERBOSITY_NAME(INFO);
-int VERBOSITY_NAME(td_requests) = VERBOSITY_NAME(INFO);
-int VERBOSITY_NAME(dc) = VERBOSITY_NAME(DEBUG) + 2;
-int VERBOSITY_NAME(files) = VERBOSITY_NAME(DEBUG) + 2;
-int VERBOSITY_NAME(mtproto) = VERBOSITY_NAME(DEBUG) + 7;
-int VERBOSITY_NAME(connections) = VERBOSITY_NAME(DEBUG) + 8;
-int VERBOSITY_NAME(raw_mtproto) = VERBOSITY_NAME(DEBUG) + 10;
-int VERBOSITY_NAME(fd) = VERBOSITY_NAME(DEBUG) + 9;
-int VERBOSITY_NAME(actor) = VERBOSITY_NAME(DEBUG) + 10;
-int VERBOSITY_NAME(buffer) = VERBOSITY_NAME(DEBUG) + 10;
-int VERBOSITY_NAME(sqlite) = VERBOSITY_NAME(DEBUG) + 10;
+LogOptions log_options;
+
+static std::atomic<int> max_callback_verbosity_level{-2};
+static std::atomic<OnLogMessageCallback> on_log_message_callback{nullptr};
+
+void set_log_message_callback(int max_verbosity_level, OnLogMessageCallback callback) {
+ if (callback == nullptr) {
+ max_verbosity_level = -2;
+ }
+
+ max_callback_verbosity_level = max_verbosity_level;
+ on_log_message_callback = callback;
+}
+
+void LogInterface::append(int log_level, CSlice slice) {
+ do_append(log_level, slice);
+ if (log_level == VERBOSITY_NAME(FATAL)) {
+ process_fatal_error(slice);
+ } else if (log_level <= max_callback_verbosity_level.load(std::memory_order_relaxed)) {
+ auto callback = on_log_message_callback.load(std::memory_order_relaxed);
+ if (callback != nullptr) {
+ callback(log_level, slice);
+ }
+ }
+}
TD_THREAD_LOCAL const char *Logger::tag_ = nullptr;
TD_THREAD_LOCAL const char *Logger::tag2_ = nullptr;
-Logger::Logger(LogInterface &log, int log_level, Slice file_name, int line_num, Slice comment, bool simple_mode)
- : Logger(log, log_level, simple_mode) {
- if (simple_mode) {
+Logger::Logger(LogInterface &log, const LogOptions &options, int log_level, Slice file_name, int line_num,
+ Slice comment)
+ : Logger(log, options, log_level) {
+ if (log_level == VERBOSITY_NAME(PLAIN) && &options == &log_options) {
+ return;
+ }
+ if (!options_.add_info) {
+ return;
+ }
+ if (ExitGuard::is_exited()) {
return;
}
- auto last_slash_ = static_cast<int32>(file_name.size()) - 1;
- while (last_slash_ >= 0 && file_name[last_slash_] != '/' && file_name[last_slash_] != '\\') {
- last_slash_--;
+ // log level
+ sb_ << '[';
+ if (static_cast<uint32>(log_level) < 10) {
+ sb_ << ' ' << static_cast<char>('0' + log_level);
+ } else {
+ sb_ << log_level;
}
- file_name = file_name.substr(last_slash_ + 1);
+ sb_ << ']';
+ // thread id
auto thread_id = get_thread_id();
+ sb_ << "[t";
+ if (static_cast<uint32>(thread_id) < 10) {
+ sb_ << ' ' << static_cast<char>('0' + thread_id);
+ } else {
+ sb_ << thread_id;
+ }
+ sb_ << ']';
- (*this) << '[';
- if (log_level < 10) {
- (*this) << ' ';
+ // timestamp
+ auto time = Clocks::system();
+ auto unix_time = static_cast<uint32>(time);
+ auto nanoseconds = static_cast<uint32>((time - unix_time) * 1e9);
+ sb_ << '[' << unix_time << '.';
+ uint32 limit = 100000000;
+ while (nanoseconds < limit && limit > 1) {
+ sb_ << '0';
+ limit /= 10;
}
- (*this) << log_level << "][t";
- if (thread_id < 10) {
- (*this) << ' ';
+ sb_ << nanoseconds << ']';
+
+ // file : line
+ if (!file_name.empty()) {
+ auto last_slash_ = static_cast<int32>(file_name.size()) - 1;
+ while (last_slash_ >= 0 && file_name[last_slash_] != '/' && file_name[last_slash_] != '\\') {
+ last_slash_--;
+ }
+ file_name = file_name.substr(last_slash_ + 1);
+ sb_ << '[' << file_name << ':' << static_cast<uint32>(line_num) << ']';
}
- (*this) << thread_id << "][" << StringBuilder::FixedDouble(Clocks::system(), 9) << "][" << file_name << ':'
- << line_num << ']';
+
+ // context from tag_
if (tag_ != nullptr && *tag_) {
- (*this) << "[#" << Slice(tag_) << "]";
+ sb_ << "[#" << Slice(tag_) << ']';
}
+
+ // context from tag2_
if (tag2_ != nullptr && *tag2_) {
- (*this) << "[!" << Slice(tag2_) << "]";
+ sb_ << "[!" << Slice(tag2_) << ']';
}
+
+ // comment (e.g. condition in LOG_IF)
if (!comment.empty()) {
- (*this) << "[&" << comment << "]";
+ sb_ << "[&" << comment << ']';
}
- (*this) << "\t";
+
+ sb_ << '\t';
}
Logger::~Logger() {
- if (!simple_mode_) {
+ if (ExitGuard::is_exited()) {
+ return;
+ }
+ if (options_.fix_newlines) {
sb_ << '\n';
auto slice = as_cslice();
if (slice.back() != '\n') {
slice.back() = '\n';
}
- }
-
- log_.append(as_cslice(), log_level_);
-}
-
-TsCerr::TsCerr() {
- enterCritical();
-}
-TsCerr::~TsCerr() {
- exitCritical();
-}
-TsCerr &TsCerr::operator<<(Slice slice) {
- auto &fd = Fd::Stderr();
- if (fd.empty()) {
- return *this;
- }
- double end_time = 0;
- while (!slice.empty()) {
- auto res = fd.write(slice);
- if (res.is_error()) {
- if (res.error().code() == EPIPE) {
- break;
- }
- // Resource temporary unavailable
- if (end_time == 0) {
- end_time = Time::now() + 0.01;
- } else if (Time::now() > end_time) {
- break;
- }
- continue;
+ while (slice.size() > 1 && slice[slice.size() - 2] == '\n') {
+ slice.back() = '\0';
+ slice = MutableCSlice(slice.begin(), slice.begin() + slice.size() - 1);
}
- slice.remove_prefix(res.ok());
+ log_.append(log_level_, slice);
+ } else {
+ log_.append(log_level_, as_cslice());
}
- return *this;
}
-void TsCerr::enterCritical() {
- while (lock_.test_and_set(std::memory_order_acquire)) {
- // spin
- }
-}
-
-void TsCerr::exitCritical() {
- lock_.clear(std::memory_order_release);
-}
-TsCerr::Lock TsCerr::lock_ = ATOMIC_FLAG_INIT;
-
-class DefaultLog : public LogInterface {
- public:
- void append(CSlice slice, int log_level) override {
+class DefaultLog final : public LogInterface {
+ void do_append(int log_level, CSlice slice) final {
#if TD_ANDROID
switch (log_level) {
case VERBOSITY_NAME(FATAL):
@@ -157,27 +173,26 @@ class DefaultLog : public LogInterface {
#elif TD_TIZEN
switch (log_level) {
case VERBOSITY_NAME(FATAL):
- dlog_print(DLOG_ERROR, DLOG_TAG, slice.c_str());
+ dlog_print(DLOG_ERROR, DLOG_TAG, "%s", slice.c_str());
break;
case VERBOSITY_NAME(ERROR):
- dlog_print(DLOG_ERROR, DLOG_TAG, slice.c_str());
+ dlog_print(DLOG_ERROR, DLOG_TAG, "%s", slice.c_str());
break;
case VERBOSITY_NAME(WARNING):
- dlog_print(DLOG_WARN, DLOG_TAG, slice.c_str());
+ dlog_print(DLOG_WARN, DLOG_TAG, "%s", slice.c_str());
break;
case VERBOSITY_NAME(INFO):
- dlog_print(DLOG_INFO, DLOG_TAG, slice.c_str());
+ dlog_print(DLOG_INFO, DLOG_TAG, "%s", slice.c_str());
break;
default:
- dlog_print(DLOG_DEBUG, DLOG_TAG, slice.c_str());
+ dlog_print(DLOG_DEBUG, DLOG_TAG, "%s", slice.c_str());
break;
}
#elif TD_EMSCRIPTEN
switch (log_level) {
case VERBOSITY_NAME(FATAL):
- emscripten_log(
- EM_LOG_ERROR | EM_LOG_CONSOLE | EM_LOG_C_STACK | EM_LOG_JS_STACK | EM_LOG_DEMANGLE | EM_LOG_FUNC_PARAMS,
- "%s", slice.c_str());
+ emscripten_log(EM_LOG_ERROR | EM_LOG_CONSOLE | EM_LOG_C_STACK | EM_LOG_JS_STACK | EM_LOG_FUNC_PARAMS, "%s",
+ slice.c_str());
EM_ASM(throw(UTF8ToString($0)), slice.c_str());
break;
case VERBOSITY_NAME(ERROR):
@@ -192,28 +207,31 @@ class DefaultLog : public LogInterface {
}
#elif !TD_WINDOWS
Slice color;
+ Slice no_color("\x1b[0m");
switch (log_level) {
case VERBOSITY_NAME(FATAL):
case VERBOSITY_NAME(ERROR):
- color = TC_RED;
+ color = Slice("\x1b[1;31m"); // red
break;
case VERBOSITY_NAME(WARNING):
- color = TC_YELLOW;
+ color = Slice("\x1b[1;33m"); // yellow
break;
case VERBOSITY_NAME(INFO):
- color = TC_CYAN;
+ color = Slice("\x1b[1;36m"); // cyan
+ break;
+ default:
+ no_color = Slice();
break;
}
- TsCerr() << color << slice << TC_EMPTY;
+ if (!slice.empty() && slice.back() == '\n') {
+ TsCerr() << color << slice.substr(0, slice.size() - 1) << no_color << "\n";
+ } else {
+ TsCerr() << color << slice << no_color;
+ }
#else
// TODO: color
TsCerr() << slice;
#endif
- if (log_level == VERBOSITY_NAME(FATAL)) {
- process_fatal_error(slice);
- }
- }
- void rotate() override {
}
};
static DefaultLog default_log;
@@ -221,18 +239,59 @@ static DefaultLog default_log;
LogInterface *const default_log_interface = &default_log;
LogInterface *log_interface = default_log_interface;
-static OnFatalErrorCallback on_fatal_error_callback = nullptr;
+void process_fatal_error(CSlice message) {
+ if (0 <= max_callback_verbosity_level.load(std::memory_order_relaxed)) {
+ auto callback = on_log_message_callback.load(std::memory_order_relaxed);
+ if (callback != nullptr) {
+ callback(0, message);
+ }
+ }
-void set_log_fatal_error_callback(OnFatalErrorCallback callback) {
- on_fatal_error_callback = callback;
+ std::abort();
}
-void process_fatal_error(CSlice message) {
- auto callback = on_fatal_error_callback;
- if (callback) {
- callback(message);
+static std::atomic<uint32> log_guard;
+
+LogGuard::LogGuard() {
+ uint32 expected = 0;
+ while (!log_guard.compare_exchange_strong(expected, 1, std::memory_order_relaxed, std::memory_order_relaxed)) {
+ // spin
+ CHECK(expected == 1);
+ expected = 0;
}
- std::abort();
}
+LogGuard::~LogGuard() {
+ CHECK(log_guard.load(std::memory_order_relaxed) == 1);
+ log_guard.store(0, std::memory_order_relaxed);
+}
+
+bool has_log_guard() {
+ return log_guard.load(std::memory_order_relaxed) == 1;
+}
+
+namespace {
+std::mutex sdl_mutex;
+int sdl_cnt = 0;
+int sdl_verbosity = 0;
+} // namespace
+
+ScopedDisableLog::ScopedDisableLog() {
+ std::unique_lock<std::mutex> guard(sdl_mutex);
+ if (sdl_cnt == 0) {
+ sdl_verbosity = set_verbosity_level(std::numeric_limits<int>::min());
+ }
+ sdl_cnt++;
+}
+
+ScopedDisableLog::~ScopedDisableLog() {
+ std::unique_lock<std::mutex> guard(sdl_mutex);
+ sdl_cnt--;
+ if (sdl_cnt == 0) {
+ set_verbosity_level(sdl_verbosity);
+ }
+}
+
+static ExitGuard exit_guard;
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.h
index 629a4f248a..b6f45eb686 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/logging.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -25,38 +25,33 @@
#include "td/utils/common.h"
#include "td/utils/port/thread_local.h"
-#include "td/utils/Slice-decl.h"
+#include "td/utils/Slice.h"
#include "td/utils/StackAllocator.h"
#include "td/utils/StringBuilder.h"
#include <atomic>
#include <type_traits>
-#define PSTR_IMPL() ::td::Logger(::td::NullLog().ref(), 0, true)
-#define PSLICE() ::td::detail::Slicify() & PSTR_IMPL()
-#define PSTRING() ::td::detail::Stringify() & PSTR_IMPL()
-#define PSLICE_SAFE() ::td::detail::SlicifySafe() & PSTR_IMPL()
-#define PSTRING_SAFE() ::td::detail::StringifySafe() & PSTR_IMPL()
-
#define VERBOSITY_NAME(x) verbosity_##x
-#define GET_VERBOSITY_LEVEL() (::td::VERBOSITY_NAME(level))
-#define SET_VERBOSITY_LEVEL(new_level) (::td::VERBOSITY_NAME(level) = (new_level))
+#define GET_VERBOSITY_LEVEL() (::td::get_verbosity_level())
+#define SET_VERBOSITY_LEVEL(new_level) (::td::set_verbosity_level(new_level))
#ifndef STRIP_LOG
#define STRIP_LOG VERBOSITY_NAME(DEBUG)
#endif
#define LOG_IS_STRIPPED(strip_level) \
- (std::integral_constant<int, VERBOSITY_NAME(strip_level)>() > std::integral_constant<int, STRIP_LOG>())
+ (::std::integral_constant<int, VERBOSITY_NAME(strip_level)>() > ::std::integral_constant<int, STRIP_LOG>())
+
+#define LOGGER(interface, options, level, comment) ::td::Logger(interface, options, level, __FILE__, __LINE__, comment)
-#define LOGGER(level, comment) \
- ::td::Logger(*::td::log_interface, VERBOSITY_NAME(level), __FILE__, __LINE__, comment, \
- VERBOSITY_NAME(level) == VERBOSITY_NAME(PLAIN))
+#define LOG_IMPL_FULL(interface, options, strip_level, runtime_level, condition, comment) \
+ LOG_IS_STRIPPED(strip_level) || runtime_level > options.get_level() || !(condition) \
+ ? (void)0 \
+ : ::td::detail::Voidify() & LOGGER(interface, options, runtime_level, comment)
-#define LOG_IMPL(strip_level, level, condition, comment) \
- LOG_IS_STRIPPED(strip_level) || VERBOSITY_NAME(level) > GET_VERBOSITY_LEVEL() || !(condition) \
- ? (void)0 \
- : ::td::detail::Voidify() & LOGGER(level, comment)
+#define LOG_IMPL(strip_level, level, condition, comment) \
+ LOG_IMPL_FULL(*::td::log_interface, ::td::log_options, strip_level, VERBOSITY_NAME(level), condition, comment)
#define LOG(level) LOG_IMPL(level, level, true, ::td::Slice())
#define LOG_IF(level, condition) LOG_IMPL(level, level, condition, #condition)
@@ -64,8 +59,6 @@
#define VLOG(level) LOG_IMPL(DEBUG, level, true, TD_DEFINE_STR(level))
#define VLOG_IF(level, condition) LOG_IMPL(DEBUG, level, condition, TD_DEFINE_STR(level) " " #condition)
-#define LOG_ROTATE() ::td::log_interface->rotate()
-
#define LOG_TAG ::td::Logger::tag_
#define LOG_TAG2 ::td::Logger::tag2_
@@ -78,25 +71,26 @@ inline bool no_return_func() {
}
// clang-format off
-#ifdef CHECK
- #undef CHECK
-#endif
+#define DUMMY_LOG_CHECK(condition) LOG_IF(NEVER, !(condition))
+
#ifdef TD_DEBUG
#if TD_MSVC
- #define CHECK(condition) \
+ #define LOG_CHECK(condition) \
__analysis_assume(!!(condition)); \
LOG_IMPL(FATAL, FATAL, !(condition), #condition)
#else
- #define CHECK(condition) LOG_IMPL(FATAL, FATAL, !(condition) && no_return_func(), #condition)
+ #define LOG_CHECK(condition) LOG_IMPL(FATAL, FATAL, !(condition) && no_return_func(), #condition)
#endif
#else
- #define CHECK(condition) LOG_IF(NEVER, !(condition))
+ #define LOG_CHECK DUMMY_LOG_CHECK
#endif
-// clang-format on
-#define UNREACHABLE() \
- LOG(FATAL); \
- ::td::process_fatal_error("Unreachable in " __FILE__ " at " TD_DEFINE_STR(__LINE__))
+#if NDEBUG
+ #define LOG_DCHECK DUMMY_LOG_CHECK
+#else
+ #define LOG_DCHECK LOG_CHECK
+#endif
+// clang-format on
constexpr int VERBOSITY_NAME(PLAIN) = -1;
constexpr int VERBOSITY_NAME(FATAL) = 0;
@@ -107,19 +101,53 @@ constexpr int VERBOSITY_NAME(DEBUG) = 4;
constexpr int VERBOSITY_NAME(NEVER) = 1024;
namespace td {
-extern int VERBOSITY_NAME(level);
-// TODO Not part of utils. Should be in some separate file
-extern int VERBOSITY_NAME(mtproto);
-extern int VERBOSITY_NAME(raw_mtproto);
-extern int VERBOSITY_NAME(connections);
-extern int VERBOSITY_NAME(dc);
-extern int VERBOSITY_NAME(fd);
-extern int VERBOSITY_NAME(net_query);
-extern int VERBOSITY_NAME(td_requests);
-extern int VERBOSITY_NAME(actor);
-extern int VERBOSITY_NAME(buffer);
-extern int VERBOSITY_NAME(files);
-extern int VERBOSITY_NAME(sqlite);
+
+struct LogOptions {
+ std::atomic<int> level{VERBOSITY_NAME(DEBUG) + 1};
+ bool fix_newlines{true};
+ bool add_info{true};
+
+ int get_level() const {
+ return level.load(std::memory_order_relaxed);
+ }
+ int set_level(int new_level) {
+ return level.exchange(new_level);
+ }
+
+ static const LogOptions &plain() {
+ static LogOptions plain_options{0, false, false};
+ return plain_options;
+ }
+
+ constexpr LogOptions() = default;
+ constexpr LogOptions(int level, bool fix_newlines, bool add_info)
+ : level(level), fix_newlines(fix_newlines), add_info(add_info) {
+ }
+
+ LogOptions(const LogOptions &other) : LogOptions(other.level.load(), other.fix_newlines, other.add_info) {
+ }
+
+ LogOptions &operator=(const LogOptions &other) {
+ if (this == &other) {
+ return *this;
+ }
+ level = other.level.load();
+ fix_newlines = other.fix_newlines;
+ add_info = other.add_info;
+ return *this;
+ }
+ LogOptions(LogOptions &&) = delete;
+ LogOptions &operator=(LogOptions &&) = delete;
+ ~LogOptions() = default;
+};
+
+extern LogOptions log_options;
+inline int set_verbosity_level(int level) {
+ return log_options.set_level(level);
+}
+inline int get_verbosity_level() {
+ return log_options.get_level();
+}
class LogInterface {
public:
@@ -129,69 +157,43 @@ class LogInterface {
LogInterface(LogInterface &&) = delete;
LogInterface &operator=(LogInterface &&) = delete;
virtual ~LogInterface() = default;
- virtual void append(CSlice slice, int log_level_) = 0;
- virtual void rotate() = 0;
-};
-class NullLog : public LogInterface {
- public:
- void append(CSlice slice, int log_level_) override {
- }
- void rotate() override {
+ void append(int log_level, CSlice slice);
+
+ virtual void after_rotation() {
}
- NullLog &ref() {
- return *this;
+
+ virtual vector<string> get_file_paths() {
+ return {};
}
+
+ virtual void do_append(int log_level, CSlice slice) = 0;
};
extern LogInterface *const default_log_interface;
extern LogInterface *log_interface;
-using OnFatalErrorCallback = void (*)(CSlice message);
-void set_log_fatal_error_callback(OnFatalErrorCallback callback);
-
[[noreturn]] void process_fatal_error(CSlice message);
-#define TC_RED "\e[1;31m"
-#define TC_BLUE "\e[1;34m"
-#define TC_CYAN "\e[1;36m"
-#define TC_GREEN "\e[1;32m"
-#define TC_YELLOW "\e[1;33m"
-#define TC_EMPTY "\e[0m"
-
-class TsCerr {
- public:
- TsCerr();
- TsCerr(const TsCerr &) = delete;
- TsCerr &operator=(const TsCerr &) = delete;
- TsCerr(TsCerr &&) = delete;
- TsCerr &operator=(TsCerr &&) = delete;
- ~TsCerr();
- TsCerr &operator<<(Slice slice);
-
- private:
- using Lock = std::atomic_flag;
- static Lock lock_;
-
- void enterCritical();
- void exitCritical();
-};
+using OnLogMessageCallback = void (*)(int verbosity_level, CSlice message);
+void set_log_message_callback(int max_verbosity_level, OnLogMessageCallback callback);
class Logger {
+ static const size_t BUFFER_SIZE = 128 * 1024;
+
public:
- static const int BUFFER_SIZE = 128 * 1024;
- Logger(LogInterface &log, int log_level, bool simple_mode = false)
+ Logger(LogInterface &log, const LogOptions &options, int log_level)
: buffer_(StackAllocator::alloc(BUFFER_SIZE))
, log_(log)
- , log_level_(log_level)
, sb_(buffer_.as_slice())
- , simple_mode_(simple_mode) {
+ , options_(options)
+ , log_level_(log_level) {
}
- Logger(LogInterface &log, int log_level, Slice file_name, int line_num, Slice comment, bool simple_mode);
+ Logger(LogInterface &log, const LogOptions &options, int log_level, Slice file_name, int line_num, Slice comment);
template <class T>
- Logger &operator<<(const T &other) {
+ Logger &operator<<(T &&other) {
sb_ << other;
return *this;
}
@@ -214,66 +216,40 @@ class Logger {
private:
decltype(StackAllocator::alloc(0)) buffer_;
LogInterface &log_;
- int log_level_;
StringBuilder sb_;
- bool simple_mode_;
+ const LogOptions &options_;
+ int log_level_;
};
-namespace detail {
-class Voidify {
+class LogGuard {
public:
- template <class T>
- void operator&(const T &) {
- }
+ LogGuard();
+ LogGuard(const LogGuard &) = delete;
+ LogGuard &operator=(const LogGuard &) = delete;
+ LogGuard(LogGuard &&) = delete;
+ LogGuard &operator=(LogGuard &&) = delete;
+ ~LogGuard();
};
-class Slicify {
+bool has_log_guard();
+
+class ScopedDisableLog {
public:
- CSlice operator&(Logger &logger) {
- return logger.as_cslice();
- }
+ ScopedDisableLog();
+ ScopedDisableLog(const ScopedDisableLog &) = delete;
+ ScopedDisableLog &operator=(const ScopedDisableLog &) = delete;
+ ScopedDisableLog(ScopedDisableLog &&) = delete;
+ ScopedDisableLog &operator=(ScopedDisableLog &&) = delete;
+ ~ScopedDisableLog();
};
-class Stringify {
+namespace detail {
+class Voidify {
public:
- string operator&(Logger &logger) {
- return logger.as_cslice().str();
+ template <class T>
+ void operator&(const T &) {
}
};
} // namespace detail
-class TsLog : public LogInterface {
- public:
- explicit TsLog(LogInterface *log) : log_(log) {
- }
- void init(LogInterface *log) {
- enter_critical();
- log_ = log;
- exit_critical();
- }
- void append(CSlice slice, int level) override {
- enter_critical();
- log_->append(slice, level);
- exit_critical();
- }
- void rotate() override {
- enter_critical();
- log_->rotate();
- exit_critical();
- }
-
- private:
- LogInterface *log_ = nullptr;
- std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
- void enter_critical() {
- while (lock_.test_and_set(std::memory_order_acquire)) {
- // spin
- }
- }
- void exit_critical() {
- lock_.clear(std::memory_order_release);
- }
-};
} // namespace td
-
-#include "td/utils/Slice.h"
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.cpp
index f3068ca6d3..df86382031 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,6 +7,7 @@
#include "td/utils/misc.h"
#include "td/utils/port/thread_local.h"
+#include "td/utils/utf8.h"
#include <algorithm>
#include <cstdlib>
@@ -16,7 +17,7 @@
namespace td {
char *str_dup(Slice str) {
- char *res = static_cast<char *>(std::malloc(str.size() + 1));
+ auto *res = static_cast<char *>(std::malloc(str.size() + 1));
if (res == nullptr) {
return nullptr;
}
@@ -25,23 +26,41 @@ char *str_dup(Slice str) {
return res;
}
-string implode(vector<string> v, char delimiter) {
+string implode(const vector<string> &v, char delimiter) {
string result;
- for (auto &str : v) {
- if (!result.empty()) {
+ for (size_t i = 0; i < v.size(); i++) {
+ if (i != 0) {
result += delimiter;
}
- result += str;
+ result += v[i];
}
return result;
}
+string lpad(string str, size_t size, char c) {
+ if (str.size() >= size) {
+ return str;
+ }
+ return string(size - str.size(), c) + str;
+}
+
+string lpad0(string str, size_t size) {
+ return lpad(std::move(str), size, '0');
+}
+
+string rpad(string str, size_t size, char c) {
+ if (str.size() >= size) {
+ return str;
+ }
+ return str + string(size - str.size(), c);
+}
+
string oneline(Slice str) {
string result;
result.reserve(str.size());
bool after_new_line = true;
for (auto c : str) {
- if (c != '\n') {
+ if (c != '\n' && c != '\r') {
if (after_new_line) {
if (c == ' ') {
continue;
@@ -49,7 +68,7 @@ string oneline(Slice str) {
after_new_line = false;
}
result += c;
- } else {
+ } else if (!after_new_line) {
after_new_line = true;
result += ' ';
}
@@ -60,10 +79,20 @@ string oneline(Slice str) {
return result;
}
+namespace detail {
+Status get_to_integer_safe_error(Slice str) {
+ auto status = Status::Error(PSLICE() << "Can't parse \"" << str << "\" as an integer");
+ if (!check_utf8(status.message())) {
+ status = Status::Error("Strings must be encoded in UTF-8");
+ }
+ return status;
+}
+} // namespace detail
+
double to_double(Slice str) {
static TD_THREAD_LOCAL std::stringstream *ss;
if (init_thread_local<std::stringstream>(ss)) {
- ss->imbue(std::locale::classic());
+ auto previous_locale = ss->imbue(std::locale::classic());
} else {
ss->str(std::string());
ss->clear();
@@ -75,4 +104,157 @@ double to_double(Slice str) {
return result;
}
+Result<string> hex_decode(Slice hex) {
+ if (hex.size() % 2 != 0) {
+ return Status::Error("Wrong hex string length");
+ }
+ string result(hex.size() / 2, '\0');
+ for (size_t i = 0; i < result.size(); i++) {
+ int high = hex_to_int(hex[i + i]);
+ int low = hex_to_int(hex[i + i + 1]);
+ if (high == 16 || low == 16) {
+ return Status::Error("Wrong hex string");
+ }
+ result[i] = static_cast<char>(high * 16 + low); // TODO implementation-defined
+ }
+ return std::move(result);
+}
+
+string hex_encode(Slice data) {
+ const char *hex = "0123456789abcdef";
+ string res;
+ res.reserve(2 * data.size());
+ for (unsigned char c : data) {
+ res.push_back(hex[c >> 4]);
+ res.push_back(hex[c & 15]);
+ }
+ return res;
+}
+
+static bool is_url_char(char c) {
+ return is_alnum(c) || c == '-' || c == '.' || c == '_' || c == '~';
+}
+
+string url_encode(Slice data) {
+ size_t length = 3 * data.size();
+ for (auto c : data) {
+ length -= 2 * is_url_char(c);
+ }
+ if (length == data.size()) {
+ return data.str();
+ }
+ string result;
+ result.reserve(length);
+ for (auto c : data) {
+ if (is_url_char(c)) {
+ result += c;
+ } else {
+ auto ch = static_cast<unsigned char>(c);
+ result += '%';
+ result += "0123456789ABCDEF"[ch / 16];
+ result += "0123456789ABCDEF"[ch % 16];
+ }
+ }
+ CHECK(result.size() == length);
+ return result;
+}
+
+size_t url_decode(Slice from, MutableSlice to, bool decode_plus_sign_as_space) {
+ size_t to_i = 0;
+ CHECK(to.size() >= from.size());
+ for (size_t from_i = 0, n = from.size(); from_i < n; from_i++) {
+ if (from[from_i] == '%' && from_i + 2 < n) {
+ int high = hex_to_int(from[from_i + 1]);
+ int low = hex_to_int(from[from_i + 2]);
+ if (high < 16 && low < 16) {
+ to[to_i++] = static_cast<char>(high * 16 + low);
+ from_i += 2;
+ continue;
+ }
+ }
+ to[to_i++] = decode_plus_sign_as_space && from[from_i] == '+' ? ' ' : from[from_i];
+ }
+ return to_i;
+}
+
+string url_decode(Slice from, bool decode_plus_sign_as_space) {
+ string to;
+ to.resize(from.size());
+ to.resize(url_decode(from, to, decode_plus_sign_as_space));
+ return to;
+}
+
+MutableSlice url_decode_inplace(MutableSlice str, bool decode_plus_sign_as_space) {
+ size_t result_size = url_decode(str, str, decode_plus_sign_as_space);
+ str.truncate(result_size);
+ return str;
+}
+
+string buffer_to_hex(Slice buffer) {
+ const char *hex = "0123456789ABCDEF";
+ string res(2 * buffer.size(), '\0');
+ for (std::size_t i = 0; i < buffer.size(); i++) {
+ auto c = buffer.ubegin()[i];
+ res[2 * i] = hex[c & 15];
+ res[2 * i + 1] = hex[c >> 4];
+ }
+ return res;
+}
+
+namespace {
+
+template <class F>
+string x_decode(Slice s, F &&f) {
+ string res;
+ for (size_t n = s.size(), i = 0; i < n; i++) {
+ if (i + 1 < n && f(s[i])) {
+ res.append(static_cast<unsigned char>(s[i + 1]), s[i]);
+ i++;
+ continue;
+ }
+ res.push_back(s[i]);
+ }
+ return res;
+}
+
+template <class F>
+string x_encode(Slice s, F &&f) {
+ string res;
+ for (size_t n = s.size(), i = 0; i < n; i++) {
+ res.push_back(s[i]);
+ if (f(s[i])) {
+ unsigned char cnt = 1;
+ while (cnt < 250 && i + cnt < n && s[i + cnt] == s[i]) {
+ cnt++;
+ }
+ res.push_back(static_cast<char>(cnt));
+ i += cnt - 1;
+ }
+ }
+ return res;
+}
+
+bool is_zero(unsigned char c) {
+ return c == 0;
+}
+
+bool is_zero_or_one(unsigned char c) {
+ return c == 0 || c == 0xff;
+}
+
+} // namespace
+
+std::string zero_encode(Slice data) {
+ return x_encode(data, is_zero);
+}
+std::string zero_decode(Slice data) {
+ return x_decode(data, is_zero);
+}
+std::string zero_one_encode(Slice data) {
+ return x_encode(data, is_zero_or_one);
+}
+std::string zero_one_decode(Slice data) {
+ return x_decode(data, is_zero_or_one);
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.h
index 62b01794ab..5d9292f43a 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/misc.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,12 +9,12 @@
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#include <cstdint>
#include <limits>
-#include <tuple>
#include <type_traits>
#include <utility>
@@ -33,75 +33,26 @@ std::pair<T, T> split(T s, char delimiter = ' ') {
}
template <class T>
-vector<T> full_split(T s, char delimiter = ' ') {
- T next;
+vector<T> full_split(T s, char delimiter = ' ', size_t max_parts = std::numeric_limits<size_t>::max()) {
vector<T> result;
- while (!s.empty()) {
- std::tie(next, s) = split(s, delimiter);
- result.push_back(next);
- }
- return result;
-}
-
-string implode(vector<string> v, char delimiter = ' ');
-
-namespace detail {
-
-template <typename T>
-struct transform_helper {
- template <class Func>
- auto transform(const T &v, const Func &f) {
- vector<decltype(f(*v.begin()))> result;
- result.reserve(v.size());
- for (auto &x : v) {
- result.push_back(f(x));
- }
+ if (s.empty()) {
return result;
}
-
- template <class Func>
- auto transform(T &&v, const Func &f) {
- vector<decltype(f(std::move(*v.begin())))> result;
- result.reserve(v.size());
- for (auto &x : v) {
- result.push_back(f(std::move(x)));
+ while (result.size() + 1 < max_parts) {
+ auto delimiter_pos = s.find(delimiter);
+ if (delimiter_pos == string::npos) {
+ break;
}
- return result;
- }
-};
-
-} // namespace detail
-template <class T, class Func>
-auto transform(T &&v, const Func &f) {
- return detail::transform_helper<std::decay_t<T>>().transform(std::forward<T>(v), f);
-}
-
-template <class T>
-void reset_to_empty(T &value) {
- using std::swap;
- std::decay_t<T> tmp;
- swap(tmp, value);
-}
-
-template <class T>
-auto append(vector<T> &destination, const vector<T> &source) {
- destination.insert(destination.end(), source.begin(), source.end());
-}
-
-template <class T>
-auto append(vector<T> &destination, vector<T> &&source) {
- if (destination.empty()) {
- destination.swap(source);
- return;
+ result.push_back(s.substr(0, delimiter_pos));
+ s = s.substr(delimiter_pos + 1);
}
- destination.reserve(destination.size() + source.size());
- for (auto &elem : source) {
- destination.push_back(std::move(elem));
- }
- reset_to_empty(source);
+ result.push_back(std::move(s));
+ return result;
}
+string implode(const vector<string> &v, char delimiter = ' ');
+
inline bool begins_with(Slice str, Slice prefix) {
return prefix.size() <= str.size() && prefix == Slice(str.data(), prefix.size());
}
@@ -118,10 +69,11 @@ inline char to_lower(char c) {
return c;
}
-inline void to_lower_inplace(MutableSlice slice) {
+inline MutableSlice to_lower_inplace(MutableSlice slice) {
for (auto &c : slice) {
c = to_lower(c);
}
+ return slice;
}
inline string to_lower(Slice slice) {
@@ -191,6 +143,12 @@ T trim(T str) {
return T(begin, end);
}
+string lpad(string str, size_t size, char c);
+
+string lpad0(string str, size_t size);
+
+string rpad(string str, size_t size, char c);
+
string oneline(Slice str);
template <class T>
@@ -205,7 +163,7 @@ std::enable_if_t<std::is_signed<T>::value, T> to_integer(Slice str) {
begin++;
}
while (begin != end && is_digit(*begin)) {
- integer_value = static_cast<unsigned_T>(integer_value * 10 + (*begin++ - '0'));
+ integer_value = static_cast<unsigned_T>(integer_value * 10 + static_cast<unsigned_T>(*begin++ - '0'));
}
if (integer_value > static_cast<unsigned_T>(std::numeric_limits<T>::max())) {
static_assert(~0 + 1 == 0, "Two's complement");
@@ -227,16 +185,20 @@ std::enable_if_t<std::is_unsigned<T>::value, T> to_integer(Slice str) {
auto begin = str.begin();
auto end = str.end();
while (begin != end && is_digit(*begin)) {
- integer_value = static_cast<T>(integer_value * 10 + (*begin++ - '0'));
+ integer_value = static_cast<T>(integer_value * 10 + static_cast<T>(*begin++ - '0'));
}
return integer_value;
}
+namespace detail {
+Status get_to_integer_safe_error(Slice str);
+} // namespace detail
+
template <class T>
Result<T> to_integer_safe(Slice str) {
auto res = to_integer<T>(str);
- if (to_string(res) != str) {
- return Status::Error(PSLICE() << "Can't parse \"" << str << "\" as number");
+ if ((PSLICE() << res) != str) {
+ return detail::get_to_integer_safe_error(str);
}
return res;
}
@@ -263,6 +225,27 @@ typename std::enable_if<std::is_unsigned<T>::value, T>::type hex_to_integer(Slic
return integer_value;
}
+template <class T>
+Result<typename std::enable_if<std::is_unsigned<T>::value, T>::type> hex_to_integer_safe(Slice str) {
+ T integer_value = 0;
+ auto begin = str.begin();
+ auto end = str.end();
+ if (begin == end) {
+ return Status::Error("String is empty");
+ }
+ while (begin != end) {
+ T digit = hex_to_int(*begin++);
+ if (digit == 16) {
+ return Status::Error("String contains non-hex digit");
+ }
+ if (integer_value > std::numeric_limits<T>::max() / 16) {
+ return Status::Error("String hex number overflows");
+ }
+ integer_value = integer_value * 16 + digit;
+ }
+ return integer_value;
+}
+
double to_double(Slice str);
template <class T>
@@ -276,11 +259,23 @@ T clamp(T value, T min_value, T max_value) {
return value;
}
+Result<string> hex_decode(Slice hex);
+
+string hex_encode(Slice data);
+
+string url_encode(Slice data);
+
+size_t url_decode(Slice from, MutableSlice to, bool decode_plus_sign_as_space);
+
+string url_decode(Slice from, bool decode_plus_sign_as_space);
+
+MutableSlice url_decode_inplace(MutableSlice str, bool decode_plus_sign_as_space);
+
// run-time checked narrowing cast (type conversion):
namespace detail {
template <class T, class U>
-struct is_same_signedness
+struct is_same_signedness final
: public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value> {};
template <class T, class Enable = void>
@@ -292,22 +287,34 @@ template <class T>
struct safe_undeflying_type<T, std::enable_if_t<std::is_enum<T>::value>> {
using type = std::underlying_type_t<T>;
};
-} // namespace detail
-template <class R, class A>
-R narrow_cast(const A &a) {
- using RT = typename detail::safe_undeflying_type<R>::type;
- using AT = typename detail::safe_undeflying_type<A>::type;
+class NarrowCast {
+ const char *file_;
+ int line_;
- static_assert(std::is_integral<RT>::value, "expected integral type to cast to");
- static_assert(std::is_integral<AT>::value, "expected integral type to cast from");
+ public:
+ NarrowCast(const char *file, int line) : file_(file), line_(line) {
+ }
- auto r = R(a);
- CHECK(A(r) == a);
- CHECK((detail::is_same_signedness<RT, AT>::value) || ((static_cast<RT>(r) < RT{}) == (static_cast<AT>(a) < AT{})));
+ template <class R, class A>
+ R cast(const A &a) {
+ using RT = typename safe_undeflying_type<R>::type;
+ using AT = typename safe_undeflying_type<A>::type;
- return r;
-}
+ static_assert(std::is_integral<RT>::value, "expected integral type to cast to");
+ static_assert(std::is_integral<AT>::value, "expected integral type to cast from");
+
+ auto r = R(a);
+ LOG_CHECK(A(r) == a) << static_cast<AT>(a) << " " << static_cast<RT>(r) << " " << file_ << " " << line_;
+ LOG_CHECK((is_same_signedness<RT, AT>::value) || ((static_cast<RT>(r) < RT{}) == (static_cast<AT>(a) < AT{})))
+ << static_cast<AT>(a) << " " << static_cast<RT>(r) << " " << file_ << " " << line_;
+
+ return r;
+ }
+};
+} // namespace detail
+
+#define narrow_cast detail::NarrowCast(__FILE__, __LINE__).cast
template <class R, class A>
Result<R> narrow_cast_safe(const A &a) {
@@ -334,4 +341,14 @@ bool is_aligned_pointer(const T *pointer) {
return (reinterpret_cast<std::uintptr_t>(static_cast<const void *>(pointer)) & (Alignment - 1)) == 0;
}
+string buffer_to_hex(Slice buffer);
+
+string zero_encode(Slice data);
+
+string zero_decode(Slice data);
+
+string zero_one_encode(Slice data);
+
+string zero_one_decode(Slice data);
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/optional.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/optional.h
index 450b60f94c..579127f625 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/optional.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/optional.h
@@ -1,36 +1,99 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+#include "td/utils/common.h"
#include "td/utils/Status.h"
+#include <type_traits>
#include <utility>
namespace td {
-template <class T>
+template <class T, bool = std::is_copy_constructible<T>::value>
class optional {
public:
optional() = default;
- template <class T1>
+ template <class T1,
+ std::enable_if_t<!std::is_same<std::decay_t<T1>, optional>::value && std::is_constructible<T, T1>::value,
+ int> = 0>
optional(T1 &&t) : impl_(std::forward<T1>(t)) {
}
- explicit operator bool() {
+
+ optional(const optional &other) {
+ if (other) {
+ impl_ = Result<T>(other.value());
+ }
+ }
+
+ optional &operator=(const optional &other) {
+ if (this == &other) {
+ return *this;
+ }
+ if (other) {
+ impl_ = Result<T>(other.value());
+ } else {
+ impl_ = Result<T>();
+ }
+ return *this;
+ }
+
+ optional(optional &&other) = default;
+ optional &operator=(optional &&other) = default;
+ ~optional() = default;
+
+ explicit operator bool() const noexcept {
return impl_.is_ok();
}
T &value() {
+ DCHECK(*this);
+ return impl_.ok_ref();
+ }
+ const T &value() const {
+ DCHECK(*this);
return impl_.ok_ref();
}
T &operator*() {
return value();
}
+ T unwrap() {
+ CHECK(*this);
+ auto res = std::move(value());
+ impl_ = {};
+ return res;
+ }
+
+ optional<T> copy() const {
+ if (*this) {
+ return value();
+ }
+ return {};
+ }
+
+ template <class... ArgsT>
+ void emplace(ArgsT &&...args) {
+ impl_.emplace(std::forward<ArgsT>(args)...);
+ }
private:
Result<T> impl_;
};
+template <typename T>
+struct optional<T, false> : optional<T, true> {
+ optional() = default;
+
+ using optional<T, true>::optional;
+
+ optional(const optional &other) = delete;
+ optional &operator=(const optional &other) = delete;
+ optional(optional &&) = default;
+ optional &operator=(optional &&) = default;
+ ~optional() = default;
+};
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/overloaded.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/overloaded.h
index 6c6186f6a2..3c54151c55 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/overloaded.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/overloaded.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -22,7 +22,7 @@ struct overload<F> : public F {
template <class F, class... Fs>
struct overload<F, Fs...>
: public overload<F>
- , overload<Fs...> {
+ , public overload<Fs...> {
overload(F f, Fs... fs) : overload<F>(f), overload<Fs...>(fs...) {
}
using overload<F>::operator();
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.cpp
index da68754a61..b20beef74f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.cpp
@@ -1,23 +1,98 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/port/Clocks.h"
+#include "td/utils/port/platform.h"
+
#include <chrono>
+#include <ctime>
+
+#if TD_PORT_POSIX
+#include <time.h>
+#endif
namespace td {
-ClocksDefault::Duration ClocksDefault::monotonic() {
+double Clocks::monotonic() {
+#if TD_PORT_POSIX
+ // use system specific functions, because std::chrono::steady_clock is steady only under Windows
+
+#ifdef CLOCK_BOOTTIME
+ {
+ static bool skip = [] {
+ struct timespec spec;
+ return clock_gettime(CLOCK_BOOTTIME, &spec) != 0;
+ }();
+ struct timespec spec;
+ if (!skip && clock_gettime(CLOCK_BOOTTIME, &spec) == 0) {
+ return static_cast<double>(spec.tv_nsec) * 1e-9 + static_cast<double>(spec.tv_sec);
+ }
+ }
+#endif
+#ifdef CLOCK_MONOTONIC_RAW
+ {
+ static bool skip = [] {
+ struct timespec spec;
+ return clock_gettime(CLOCK_MONOTONIC_RAW, &spec) != 0;
+ }();
+ struct timespec spec;
+ if (!skip && clock_gettime(CLOCK_MONOTONIC_RAW, &spec) == 0) {
+ return static_cast<double>(spec.tv_nsec) * 1e-9 + static_cast<double>(spec.tv_sec);
+ }
+ }
+#endif
+
+#endif
+
auto duration = std::chrono::steady_clock::now().time_since_epoch();
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
}
-ClocksDefault::Duration ClocksDefault::system() {
+double Clocks::system() {
auto duration = std::chrono::system_clock::now().time_since_epoch();
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
}
+int Clocks::tz_offset() {
+ // not thread-safe on POSIX, so calculate the offset only once
+ static int offset = [] {
+ auto now = std::time(nullptr);
+
+ auto time_ptr = std::localtime(&now);
+ if (time_ptr == nullptr) {
+ return 0;
+ }
+ auto local_time = *time_ptr;
+
+ time_ptr = std::gmtime(&now);
+ if (time_ptr == nullptr) {
+ return 0;
+ }
+ auto utc_time = *time_ptr;
+
+ int minute_offset = local_time.tm_min - utc_time.tm_min;
+ int hour_offset = local_time.tm_hour - utc_time.tm_hour;
+ int day_offset = local_time.tm_mday - utc_time.tm_mday;
+ if (day_offset >= 20) {
+ day_offset = -1;
+ } else if (day_offset <= -20) {
+ day_offset = 1;
+ }
+ int sec_offset = day_offset * 86400 + hour_offset * 3600 + minute_offset * 60;
+ if (sec_offset >= 15 * 3600 || sec_offset <= -15 * 3600) {
+ return 0;
+ }
+ return sec_offset / 900 * 900; // round to 900 just in case
+ }();
+ return offset;
+}
+
+namespace detail {
+int init_tz_offset_private = Clocks::tz_offset();
+} // namespace detail
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.h
index a4270df4ad..d663d129d7 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Clocks.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,21 +8,12 @@
namespace td {
-class ClocksBase {
- public:
- using Duration = double;
-};
-
-// TODO: (maybe) write system specific functions.
-class ClocksDefault {
- public:
- using Duration = ClocksBase::Duration;
+struct Clocks {
+ static double monotonic();
- static Duration monotonic();
+ static double system();
- static Duration system();
+ static int tz_offset();
};
-using Clocks = ClocksDefault;
-
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/CxCli.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/CxCli.h
index b7f01fa401..f88ef5bad8 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/CxCli.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/CxCli.h
@@ -1,28 +1,37 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+#pragma managed(push, off)
#include "td/utils/port/config.h"
+#pragma managed(pop)
#include "td/utils/common.h"
-#undef small
#if TD_WINRT
+#pragma managed(push, off)
#include "td/utils/port/wstring_convert.h"
+#pragma managed(pop)
#include "collection.h"
+#pragma managed(push, off)
#include <cstdint>
#include <map>
#include <mutex>
+#pragma managed(pop)
+
+#undef small
#define REF_NEW ref new
#define CLRCALL
+#define DEPRECATED_ATTRIBUTE(message) \
+ ::Windows::Foundation::Metadata::Deprecated(message, ::Windows::Foundation::Metadata::DeprecationType::Deprecate, 0x0)
namespace CxCli {
@@ -38,8 +47,9 @@ using Platform::String;
using Platform::NullReferenceException;
-template <class Key, class Value> class ConcurrentDictionary {
-public:
+template <class Key, class Value>
+class ConcurrentDictionary {
+ public:
bool TryGetValue(Key key, Value &value) {
std::lock_guard<std::mutex> guard(mutex_);
auto it = impl_.find(key);
@@ -59,11 +69,12 @@ public:
impl_.erase(it);
return true;
}
- Value &operator [] (Key key) {
+ Value &operator[](Key key) {
std::lock_guard<std::mutex> guard(mutex_);
return impl_[key];
}
-private:
+
+ private:
std::mutex mutex_;
std::map<Key, Value> impl_;
};
@@ -72,26 +83,31 @@ inline std::int64_t Increment(volatile std::int64_t &value) {
return InterlockedIncrement64(&value);
}
-inline std::string string_to_unmanaged(String^ str) {
+inline std::string string_to_unmanaged(String ^ str) {
if (!str) {
return std::string();
}
- return td::from_wstring(str->Data(), str->Length()).ok();
+ auto r_unmanaged_str = td::from_wstring(str->Data(), str->Length());
+ if (r_unmanaged_str.is_error()) {
+ return std::string();
+ }
+ return r_unmanaged_str.move_as_ok();
}
-inline String^ string_from_unmanaged(const std::string &from) {
+inline String ^ string_from_unmanaged(const std::string &from) {
auto tmp = td::to_wstring(from).ok();
return REF_NEW String(tmp.c_str(), static_cast<unsigned>(tmp.size()));
}
-} // namespace CxCli
+} // namespace CxCli
#elif TD_CLI
-#include <msclr\marshal_cppstd.h>
+#undef small
#define REF_NEW gcnew
#define CLRCALL __clrcall
+#define DEPRECATED_ATTRIBUTE(message) System::ObsoleteAttribute(message)
namespace CxCli {
@@ -113,21 +129,34 @@ using System::NullReferenceException;
using System::Collections::Concurrent::ConcurrentDictionary;
-inline std::int64_t Increment(std::int64_t %value) {
+inline std::int64_t Increment(std::int64_t % value) {
return System::Threading::Interlocked::Increment(value);
}
-inline std::string string_to_unmanaged(String^ str) {
- if (!str) {
+inline std::string string_to_unmanaged(String ^ str) {
+ if (!str || str->Length == 0) {
return std::string();
}
- return msclr::interop::marshal_as<std::string>(str);
+
+ Array<System::Byte> ^ bytes = System::Text::Encoding::UTF8->GetBytes(str);
+ cli::pin_ptr<System::Byte> pinned_ptr = &bytes[0];
+ std::string result(reinterpret_cast<const char *>(&pinned_ptr[0]), bytes->Length);
+ return result;
}
-inline String^ string_from_unmanaged(const std::string &from) {
- return msclr::interop::marshal_as<String^>(from);
+inline String ^ string_from_unmanaged(const std::string &from) {
+ if (from.empty()) {
+ return String::Empty;
+ }
+
+ Array<System::Byte> ^ bytes = REF_NEW Vector<System::Byte>(static_cast<ArrayIndexType>(from.size()));
+ cli::pin_ptr<System::Byte> pinned_ptr = &bytes[0];
+ for (size_t i = 0; i < from.size(); ++i) {
+ pinned_ptr[i] = from[i];
+ }
+ return System::Text::Encoding::UTF8->GetString(bytes);
}
-} // namespace CxCli
+} // namespace CxCli
#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFd.h
index ba2edabefd..8972259102 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFd.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFd.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFdBase.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFdBase.h
index e119a3c0eb..d4f586c303 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFdBase.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/EventFdBase.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,7 +7,7 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/Status.h"
namespace td {
@@ -23,10 +23,10 @@ class EventFdBase {
virtual void init() = 0;
virtual bool empty() = 0;
virtual void close() = 0;
- virtual const Fd &get_fd() const = 0;
- virtual Fd &get_fd() = 0;
+ virtual PollableFdInfo &get_poll_info() = 0;
virtual Status get_pending_error() TD_WARN_UNUSED_RESULT = 0;
virtual void release() = 0;
virtual void acquire() = 0;
+ virtual void wait(int timeout_ms) = 0;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.cpp
deleted file mode 100644
index cb4cb27306..0000000000
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.cpp
+++ /dev/null
@@ -1,1104 +0,0 @@
-//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-#include "td/utils/port/Fd.h"
-
-#include "td/utils/common.h"
-#include "td/utils/format.h"
-#include "td/utils/logging.h"
-#include "td/utils/misc.h"
-#include "td/utils/Observer.h"
-
-#if TD_PORT_POSIX
-
-#include <atomic>
-
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#endif
-
-#if TD_PORT_WINDOWS
-
-#include "td/utils/buffer.h"
-#include "td/utils/misc.h"
-
-#include <cstring>
-
-#endif
-
-namespace td {
-
-#if TD_PORT_POSIX
-
-Fd::InfoSet::InfoSet() {
- get_info(0).refcnt = 1;
- get_info(1).refcnt = 1;
- get_info(2).refcnt = 1;
-}
-Fd::Info &Fd::InfoSet::get_info(int32 id) {
- CHECK(0 <= id && id < InfoSet::MAX_FD) << tag("fd", id);
- return fd_array_[id];
-}
-Fd::InfoSet Fd::fd_info_set_;
-
-// TODO(bug) if constuctor call tries to output something to the LOG it will fail, because log is not initialized
-Fd Fd::stderr_(2, Mode::Reference);
-Fd Fd::stdout_(1, Mode::Reference);
-Fd Fd::stdin_(0, Mode::Reference);
-
-Fd::Fd() = default;
-
-Fd::Fd(int fd, Mode mode) : mode_(mode), fd_(fd) {
- auto *info = get_info();
- int old_ref_cnt = info->refcnt.load(std::memory_order_relaxed);
- if (old_ref_cnt == 0) {
- old_ref_cnt = info->refcnt.load(std::memory_order_acquire);
- CHECK(old_ref_cnt == 0);
- CHECK(mode_ == Mode::Owner) << tag("fd", fd_);
- VLOG(fd) << "FD created [fd:" << fd_ << "]";
-
- auto fcntl_res = fcntl(fd_, F_GETFD);
- auto fcntl_errno = errno;
- LOG_IF(FATAL, fcntl_res == -1) << Status::PosixError(fcntl_errno, "fcntl F_GET_FD failed");
-
- info->refcnt.store(1, std::memory_order_relaxed);
- CHECK(mode_ != Mode::Reference);
- CHECK(info->observer == nullptr);
- info->flags = 0;
- info->observer = nullptr;
- } else {
- CHECK(mode_ == Mode::Reference) << tag("fd", fd_);
- auto fcntl_res = fcntl(fd_, F_GETFD);
- auto fcntl_errno = errno;
- LOG_IF(FATAL, fcntl_res == -1) << Status::PosixError(fcntl_errno, "fcntl F_GET_FD failed");
-
- CHECK(mode_ == Mode::Reference);
- info->refcnt.fetch_add(1, std::memory_order_relaxed);
- }
-}
-
-int Fd::move_as_native_fd() {
- clear_info();
- auto res = fd_;
- fd_ = -1;
- return res;
-}
-
-Fd::~Fd() {
- close();
-}
-
-Fd::Fd(Fd &&other) {
- fd_ = other.fd_;
- mode_ = other.mode_;
- other.fd_ = -1;
-}
-
-Fd &Fd::operator=(Fd &&other) {
- if (this != &other) {
- close();
-
- fd_ = other.fd_;
- mode_ = other.mode_;
- other.fd_ = -1;
- }
- return *this;
-}
-
-Fd Fd::clone() const {
- return Fd(fd_, Mode::Reference);
-}
-
-Fd &Fd::Stderr() {
- return stderr_;
-}
-Fd &Fd::Stdout() {
- return stdout_;
-}
-Fd &Fd::Stdin() {
- return stdin_;
-}
-
-Status Fd::duplicate(const Fd &from, Fd &to) {
- CHECK(!from.empty());
- CHECK(!to.empty());
- if (dup2(from.get_native_fd(), to.get_native_fd()) == -1) {
- return OS_ERROR("dup2 failed");
- }
- return Status::OK();
-}
-
-bool Fd::empty() const {
- return fd_ == -1;
-}
-
-const Fd &Fd::get_fd() const {
- return *this;
-}
-
-Fd &Fd::get_fd() {
- return *this;
-}
-
-int Fd::get_native_fd() const {
- CHECK(!empty());
- return fd_;
-}
-
-void Fd::set_observer(ObserverBase *observer) {
- auto *info = get_info();
- CHECK(observer == nullptr || info->observer == nullptr);
- info->observer = observer;
-}
-
-ObserverBase *Fd::get_observer() const {
- auto *info = get_info();
- return info->observer;
-}
-
-void Fd::close_ref() {
- CHECK(mode_ == Mode::Reference);
- auto *info = get_info();
-
- int old_ref_cnt = info->refcnt.fetch_sub(1, std::memory_order_relaxed);
- CHECK(old_ref_cnt > 1) << tag("fd", fd_);
- fd_ = -1;
-}
-
-void Fd::close_own() {
- CHECK(mode_ == Mode::Owner);
- VLOG(fd) << "FD closed [fd:" << fd_ << "]";
-
- clear_info();
- ::close(fd_);
- fd_ = -1;
-}
-
-void Fd::close() {
- if (!empty()) {
- switch (mode_) {
- case Mode::Reference:
- close_ref();
- break;
- case Mode::Owner:
- close_own();
- break;
- }
- }
-}
-
-Fd::Info *Fd::get_info() {
- CHECK(!empty());
- return &fd_info_set_.get_info(fd_);
-}
-
-const Fd::Info *Fd::get_info() const {
- CHECK(!empty());
- return &fd_info_set_.get_info(fd_);
-}
-
-void Fd::clear_info() {
- CHECK(!empty());
- CHECK(mode_ != Mode::Reference);
-
- auto *info = get_info();
- int old_ref_cnt = info->refcnt.load(std::memory_order_relaxed);
- CHECK(old_ref_cnt == 1);
- info->flags = 0;
- info->observer = nullptr;
- info->refcnt.store(0, std::memory_order_release);
-}
-
-void Fd::update_flags_notify(Flags flags) {
- update_flags_inner(flags, true);
-}
-
-void Fd::update_flags(Flags flags) {
- update_flags_inner(flags, false);
-}
-
-void Fd::update_flags_inner(int32 new_flags, bool notify_flag) {
- if (new_flags & Error) {
- new_flags |= Error;
- new_flags |= Close;
- }
- auto *info = get_info();
- int32 &flags = info->flags;
- int32 old_flags = flags;
- flags |= new_flags;
- if (new_flags & Close) {
- // TODO: ???
- flags &= ~Write;
- }
- if (flags != old_flags) {
- VLOG(fd) << "Update flags " << tag("fd", fd_) << tag("from", format::as_binary(old_flags))
- << tag("to", format::as_binary(flags));
- }
- if (flags != old_flags && notify_flag) {
- auto observer = info->observer;
- if (observer != nullptr) {
- observer->notify();
- }
- }
-}
-
-Fd::Flags Fd::get_flags() const {
- return get_info()->flags;
-}
-
-void Fd::clear_flags(Flags flags) {
- get_info()->flags &= ~flags;
-}
-
-bool Fd::has_pending_error() const {
- return (get_flags() & Fd::Flag::Error) != 0;
-}
-
-Status Fd::get_pending_error() {
- if (!has_pending_error()) {
- return Status::OK();
- }
- clear_flags(Fd::Error);
- int error = 0;
- socklen_t errlen = sizeof(error);
- if (getsockopt(fd_, SOL_SOCKET, SO_ERROR, static_cast<void *>(&error), &errlen) == 0) {
- if (error == 0) {
- return Status::OK();
- }
- return Status::PosixError(error, PSLICE() << "Error on socket [fd_ = " << fd_ << "]");
- }
- auto status = OS_SOCKET_ERROR(PSLICE() << "Can't load error on socket [fd_ = " << fd_ << "]");
- LOG(INFO) << "Can't load pending socket error: " << status;
- return status;
-}
-
-Result<size_t> Fd::write_unsafe(Slice slice) {
- int native_fd = get_native_fd();
- auto write_res = skip_eintr([&] { return ::write(native_fd, slice.begin(), slice.size()); });
- auto write_errno = errno;
- if (write_res >= 0) {
- return narrow_cast<size_t>(write_res);
- }
- return Status::PosixError(write_errno, PSLICE() << "Write to fd " << native_fd << " has failed");
-}
-
-Result<size_t> Fd::write(Slice slice) {
- int native_fd = get_native_fd();
- auto write_res = skip_eintr([&] { return ::write(native_fd, slice.begin(), slice.size()); });
- auto write_errno = errno;
- if (write_res >= 0) {
- return narrow_cast<size_t>(write_res);
- }
-
- if (write_errno == EAGAIN
-#if EAGAIN != EWOULDBLOCK
- || write_errno == EWOULDBLOCK
-#endif
- ) {
- clear_flags(Write);
- return 0;
- }
-
- auto error = Status::PosixError(write_errno, PSLICE() << "Write to fd " << native_fd << " has failed");
- switch (write_errno) {
- case EBADF:
- case ENXIO:
- case EFAULT:
- case EINVAL:
- LOG(FATAL) << error;
- UNREACHABLE();
- default:
- LOG(WARNING) << error;
- // fallthrough
- case ECONNRESET:
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENETDOWN:
- case ENETUNREACH:
- case ENOSPC:
- case EPIPE:
- clear_flags(Write);
- update_flags(Close);
- return std::move(error);
- }
-}
-
-Result<size_t> Fd::read(MutableSlice slice) {
- int native_fd = get_native_fd();
- CHECK(slice.size() > 0);
- auto read_res = skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
- auto read_errno = errno;
- if (read_res >= 0) {
- if (read_res == 0) {
- errno = 0;
- clear_flags(Read);
- update_flags(Close);
- }
- return narrow_cast<size_t>(read_res);
- }
- if (read_errno == EAGAIN
-#if EAGAIN != EWOULDBLOCK
- || read_errno == EWOULDBLOCK
-#endif
- ) {
- clear_flags(Read);
- return 0;
- }
- auto error = Status::PosixError(read_errno, PSLICE() << "Read from fd " << native_fd << " has failed");
- switch (read_errno) {
- case EISDIR:
- case EBADF:
- case ENXIO:
- case EFAULT:
- case EINVAL:
- case ENOTCONN:
- LOG(FATAL) << error;
- UNREACHABLE();
- default:
- LOG(WARNING) << error;
- // fallthrough
- case EIO:
- case ENOBUFS:
- case ENOMEM:
- case ECONNRESET:
- case ETIMEDOUT:
- clear_flags(Read);
- update_flags(Close);
- return std::move(error);
- }
-}
-
-Status Fd::set_is_blocking(bool is_blocking) {
- auto old_flags = fcntl(fd_, F_GETFL);
- if (old_flags == -1) {
- return OS_SOCKET_ERROR("Failed to get socket flags");
- }
- auto new_flags = is_blocking ? old_flags & ~O_NONBLOCK : old_flags | O_NONBLOCK;
- if (new_flags != old_flags && fcntl(fd_, F_SETFL, new_flags) == -1) {
- return OS_SOCKET_ERROR("Failed to set socket flags");
- }
-
- return Status::OK();
-}
-
-#endif
-
-#if TD_PORT_WINDOWS
-
-class Fd::FdImpl {
- public:
- FdImpl(Fd::Type type, HANDLE handle)
- : type_(type), handle_(handle), async_mode_(type_ == Fd::Type::EventFd || type_ == Fd::Type::StdinFileFd) {
- init();
- }
- FdImpl(Fd::Type type, SOCKET socket, int socket_family)
- : type_(type), socket_(socket), socket_family_(socket_family), async_mode_(true) {
- init();
- }
-
- FdImpl(const FdImpl &) = delete;
- FdImpl &operator=(const FdImpl &) = delete;
- FdImpl(FdImpl &&) = delete;
- FdImpl &operator=(FdImpl &&) = delete;
-
- ~FdImpl() {
- close();
- }
- void set_observer(ObserverBase *observer) {
- observer_ = observer;
- }
- ObserverBase *get_observer() const {
- return observer_;
- }
-
- void update_flags_notify(Fd::Flags flags) {
- update_flags_inner(flags, true);
- }
-
- void update_flags(Fd::Flags flags) {
- update_flags_inner(flags, false);
- }
-
- void update_flags_inner(int32 new_flags, bool notify_flag) {
- if (new_flags & Fd::Error) {
- new_flags |= Fd::Error;
- new_flags |= Fd::Close;
- }
- int32 old_flags = flags_;
- flags_ |= new_flags;
- if (new_flags & Fd::Close) {
- // TODO: ???
- flags_ &= ~Fd::Write;
- internal_flags_ &= ~Fd::Write;
- }
- if (flags_ != old_flags) {
- VLOG(fd) << "Update flags " << tag("fd", get_io_handle()) << tag("from", format::as_binary(old_flags))
- << tag("to", format::as_binary(flags_));
- }
- if (flags_ != old_flags && notify_flag) {
- auto observer = get_observer();
- if (observer != nullptr) {
- observer->notify();
- }
- }
- }
-
- int32 get_flags() const {
- return flags_;
- }
-
- void clear_flags(Fd::Flags mask) {
- flags_ &= ~mask;
- }
-
- Status get_pending_error() {
- if (!has_pending_error()) {
- return Status::OK();
- }
- clear_flags(Fd::Error);
- return std::move(pending_error_);
- }
- bool has_pending_error() const {
- return (get_flags() & Fd::Flag::Error) != 0;
- }
-
- HANDLE get_read_event() {
- if (type() == Fd::Type::StdinFileFd) {
- return get_io_handle();
- }
- return read_event_;
- }
- void on_read_event() {
- if (type_ == Fd::Type::StdinFileFd) {
- return try_read_stdin();
- }
- ResetEvent(read_event_);
- if (type_ == Fd::Type::EventFd) {
- return update_flags_notify(Fd::Flag::Read);
- }
- if (type_ == Fd::Type::SocketFd && !connected_) {
- on_connect_ready();
- } else {
- if (!async_read_flag_) {
- return;
- }
-
- if (type_ == Fd::Type::ServerSocketFd) {
- on_accept_ready();
- } else {
- on_read_ready();
- }
- }
- loop();
- }
- HANDLE get_write_event() {
- return write_event_;
- }
- void on_write_event() {
- CHECK(async_write_flag_);
- ResetEvent(write_event_);
- on_write_ready();
- loop();
- }
-
- SOCKET get_native_socket() const {
- return socket_;
- }
-
- HANDLE get_io_handle() const {
- CHECK(!empty());
- if (type() == Fd::Type::FileFd || type() == Fd::Type::StdinFileFd) {
- return handle_;
- }
- return reinterpret_cast<HANDLE>(socket_);
- }
-
- Result<size_t> write(Slice slice) TD_WARN_UNUSED_RESULT {
- if (async_mode_) {
- return write_async(slice);
- } else {
- return write_sync(slice);
- }
- }
-
- Result<size_t> read(MutableSlice slice) TD_WARN_UNUSED_RESULT {
- if (async_mode_) {
- return read_async(slice);
- } else {
- return read_sync(slice);
- }
- }
-
- Result<size_t> write_async(Slice slice) TD_WARN_UNUSED_RESULT {
- CHECK(async_mode_);
- output_writer_.append(slice);
- output_reader_.sync_with_writer();
- loop();
- return slice.size();
- }
- Result<size_t> write_sync(Slice slice) TD_WARN_UNUSED_RESULT {
- CHECK(!async_mode_);
- DWORD bytes_written = 0;
- auto res = WriteFile(get_io_handle(), slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_written, nullptr);
- if (!res) {
- return OS_ERROR("Failed to write_sync");
- }
- return bytes_written;
- }
- Result<size_t> read_async(MutableSlice slice) TD_WARN_UNUSED_RESULT {
- CHECK(async_mode_);
- auto res = input_reader_.advance(min(slice.size(), input_reader_.size()), slice);
- if (res == 0) {
- clear_flags(Fd::Flag::Read);
- }
- return res;
- }
- Result<size_t> read_sync(MutableSlice slice) TD_WARN_UNUSED_RESULT {
- CHECK(!async_mode_);
- DWORD bytes_read = 0;
- auto res = ReadFile(get_io_handle(), slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, nullptr);
- if (!res) {
- return OS_ERROR("Failed to read_sync");
- }
- if (bytes_read == 0) {
- clear_flags(Fd::Flag::Read);
- }
- return bytes_read;
- }
-
- // for ServerSocket
- Result<Fd> accept() {
- if (accepted_.empty()) {
- clear_flags(Fd::Flag::Read);
- return Status::Error(-1, "Operation would block");
- }
- auto res = std::move(accepted_.back());
- accepted_.pop_back();
- return std::move(res);
- }
-
- void connect(const IPAddress &addr) {
- CHECK(!connected_);
- CHECK(type_ == Fd::Type::SocketFd);
- DWORD bytes_read;
- std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
- read_overlapped_.hEvent = read_event_;
- LPFN_CONNECTEX ConnectExPtr = nullptr;
- GUID guid = WSAID_CONNECTEX;
- DWORD numBytes;
- int error = ::WSAIoctl(socket_, SIO_GET_EXTENSION_FUNCTION_POINTER, static_cast<void *>(&guid), sizeof(guid),
- static_cast<void *>(&ConnectExPtr), sizeof(ConnectExPtr), &numBytes, nullptr, nullptr);
- if (error) {
- return on_error(OS_SOCKET_ERROR("WSAIoctl failed"), Fd::Flag::Read);
- }
- auto status = ConnectExPtr(socket_, addr.get_sockaddr(), narrow_cast<int>(addr.get_sockaddr_len()), nullptr, 0,
- &bytes_read, &read_overlapped_);
- if (status != 0) {
- ResetEvent(read_event_);
- connected_ = true;
- update_flags_notify(Fd::Flag::Read);
- return;
- }
-
- auto last_error = GetLastError();
- if (last_error == ERROR_IO_PENDING) {
- return;
- }
- on_error(OS_SOCKET_ERROR("ConnectEx failed"), Fd::Flag::Read);
- }
-
- // for EventFd
- void release() {
- CHECK(type_ == Fd::Type::EventFd);
- SetEvent(read_event_);
- }
-
- void acquire() {
- CHECK(type_ == Fd::Type::EventFd);
- ResetEvent(read_event_);
- clear_flags(Fd::Flag::Read);
- }
-
- // TODO: interface for BufferedFd optimization.
-
- bool empty() const {
- return type() == Fd::Type::Empty;
- }
- void close() {
- if (empty()) {
- return;
- }
- switch (type()) {
- case Fd::Type::StdinFileFd:
- case Fd::Type::FileFd: {
- if (!CloseHandle(handle_)) {
- auto error = OS_ERROR("Failed to close file");
- LOG(ERROR) << error;
- }
- handle_ = INVALID_HANDLE_VALUE;
- break;
- }
- case Fd::Type::ServerSocketFd:
- case Fd::Type::SocketFd: {
- if (closesocket(socket_) != 0) {
- auto error = OS_SOCKET_ERROR("Failed to close socket");
- LOG(ERROR) << error;
- }
- socket_ = INVALID_SOCKET;
- break;
- }
- case Fd::Type::EventFd:
- break;
- default:
- UNREACHABLE();
- }
-
- if (read_event_ != INVALID_HANDLE_VALUE) {
- if (!CloseHandle(read_event_)) {
- auto error = OS_ERROR("Failed to close event");
- LOG(ERROR) << error;
- }
- read_event_ = INVALID_HANDLE_VALUE;
- }
- if (write_event_ != INVALID_HANDLE_VALUE) {
- if (!CloseHandle(write_event_)) {
- auto error = OS_ERROR("Failed to close event");
- LOG(ERROR) << error;
- }
- write_event_ = INVALID_HANDLE_VALUE;
- }
-
- type_ = Fd::Type::Empty;
- }
-
- private:
- Fd::Type type_;
- HANDLE handle_ = INVALID_HANDLE_VALUE;
- SOCKET socket_ = INVALID_SOCKET;
-
- int socket_family_ = 0;
-
- bool async_mode_ = false;
-
- ObserverBase *observer_ = nullptr;
- Fd::Flags flags_ = Fd::Flag::Write;
- Status pending_error_;
- Fd::Flags internal_flags_ = Fd::Flag::Write | Fd::Flag::Read;
-
- HANDLE read_event_ = INVALID_HANDLE_VALUE; // used by WineventPoll
- bool async_read_flag_ = false; // do we have pending read?
- OVERLAPPED read_overlapped_;
- ChainBufferWriter input_writer_;
- ChainBufferReader input_reader_ = input_writer_.extract_reader();
-
- bool connected_ = false;
-
- std::vector<Fd> accepted_;
- SOCKET accept_socket_ = INVALID_SOCKET;
- static constexpr size_t MAX_ADDR_SIZE = sizeof(sockaddr_in6) + 16;
- char addr_buf_[MAX_ADDR_SIZE * 2];
-
- HANDLE write_event_ = INVALID_HANDLE_VALUE; // used by WineventPoll
- bool async_write_flag_ = false; // do we have pending write?
- OVERLAPPED write_overlapped_;
- ChainBufferWriter output_writer_;
- ChainBufferReader output_reader_ = output_writer_.extract_reader();
-
- void init() {
- if (async_mode_) {
- if (type_ != Fd::Type::EventFd) {
- write_event_ = CreateEventW(nullptr, true, false, nullptr);
- }
- read_event_ = CreateEventW(nullptr, true, false, nullptr);
- loop();
- }
- }
-
- Fd::Type type() const {
- return type_;
- }
-
- void on_error(Status error, Fd::Flag flag) {
- VLOG(fd) << tag("fd", get_io_handle()) << error;
- pending_error_ = std::move(error);
- internal_flags_ &= ~flag;
- update_flags_notify(Fd::Flag::Error);
- }
- void on_eof() {
- internal_flags_ &= ~Fd::Flag::Read;
- update_flags_notify(Fd::Flag::Close);
- }
-
- void on_read_ready() {
- async_read_flag_ = false;
- DWORD bytes_read;
- auto status = GetOverlappedResult(get_io_handle(), &read_overlapped_, &bytes_read, false);
- if (status == 0) {
- return on_error(OS_ERROR("ReadFile failed"), Fd::Flag::Read);
- }
-
- VLOG(fd) << "Read " << tag("fd", get_io_handle()) << tag("size", bytes_read);
- if (bytes_read == 0) { // eof
- return on_eof();
- }
- input_writer_.confirm_append(bytes_read);
- input_reader_.sync_with_writer();
- update_flags_notify(Fd::Flag::Read);
- }
- void on_write_ready() {
- async_write_flag_ = false;
- DWORD bytes_written;
- auto status = GetOverlappedResult(get_io_handle(), &write_overlapped_, &bytes_written, false);
- if (status == 0) {
- return on_error(OS_ERROR("WriteFile failed"), Fd::Flag::Write);
- }
- if (bytes_written != 0) {
- VLOG(fd) << "Write " << tag("fd", get_io_handle()) << tag("size", bytes_written);
- output_reader_.confirm_read(bytes_written);
- update_flags_notify(Fd::Flag::Write);
- }
- }
-
- void on_accept_ready() {
- async_read_flag_ = false;
- DWORD bytes_read;
- auto status = GetOverlappedResult(get_io_handle(), &read_overlapped_, &bytes_read, false);
- if (status == 0) {
- return on_error(OS_ERROR("AcceptEx failed"), Fd::Flag::Write);
- }
- accepted_.push_back(Fd::create_socket_fd(accept_socket_));
- accept_socket_ = INVALID_SOCKET;
- update_flags_notify(Fd::Flag::Read);
- }
-
- void on_connect_ready() {
- async_read_flag_ = false;
- DWORD bytes_read;
- VLOG(fd) << "on_connect_ready";
- auto status = GetOverlappedResult(get_io_handle(), &read_overlapped_, &bytes_read, false);
- if (status == 0) {
- return on_error(OS_ERROR("ConnectEx failed"), Fd::Flag::Write);
- }
- connected_ = true;
- VLOG(fd) << "connected = true";
- }
-
- void try_read_stdin() {
- }
- void try_start_read() {
- auto dest = input_writer_.prepare_append();
- DWORD bytes_read;
- std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
- read_overlapped_.hEvent = read_event_;
- VLOG(fd) << "try_read..";
- auto status =
- ReadFile(get_io_handle(), dest.data(), narrow_cast<DWORD>(dest.size()), &bytes_read, &read_overlapped_);
- if (status != 0) { // ok
- ResetEvent(read_event_);
- VLOG(fd) << "Read " << tag("fd", get_io_handle()) << tag("size", bytes_read);
- if (bytes_read == 0) { // eof
- return on_eof();
- }
- input_writer_.confirm_append(bytes_read);
- input_reader_.sync_with_writer();
- update_flags_notify(Fd::Flag::Read);
- return;
- }
- auto last_error = GetLastError();
- if (last_error == ERROR_IO_PENDING) {
- async_read_flag_ = true;
- return;
- }
- on_error(OS_ERROR("ReadFile failed"), Fd::Flag::Read);
- }
-
- void try_start_write() {
- auto dest = output_reader_.prepare_read();
- DWORD bytes_written;
- std::memset(&write_overlapped_, 0, sizeof(write_overlapped_));
- write_overlapped_.hEvent = write_event_;
- VLOG(fd) << "try_start_write";
- auto status =
- WriteFile(get_io_handle(), dest.data(), narrow_cast<DWORD>(dest.size()), &bytes_written, &write_overlapped_);
- if (status != 0) { // ok
- VLOG(fd) << "Write " << tag("fd", get_io_handle()) << tag("size", bytes_written);
- ResetEvent(write_event_);
- output_reader_.confirm_read(bytes_written);
- update_flags_notify(Fd::Flag::Write);
- return;
- }
- auto last_error = GetLastError();
- if (last_error == ERROR_IO_PENDING) {
- VLOG(fd) << "try_start_write: ERROR_IO_PENDING";
- async_write_flag_ = true;
- return;
- }
- CHECK(WaitForSingleObject(write_event_, 0) != WAIT_OBJECT_0);
- on_error(OS_ERROR("WriteFile failed"), Fd::Flag::Write);
- }
-
- void try_start_accept() {
- if (async_read_flag_ == true) {
- return;
- }
- accept_socket_ = socket(socket_family_, SOCK_STREAM, 0);
- DWORD bytes_read;
- std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
- read_overlapped_.hEvent = read_event_;
- auto status =
- AcceptEx(socket_, accept_socket_, addr_buf_, 0, MAX_ADDR_SIZE, MAX_ADDR_SIZE, &bytes_read, &read_overlapped_);
- if (status != 0) {
- ResetEvent(read_event_);
- accepted_.push_back(Fd::create_socket_fd(accept_socket_));
- accept_socket_ = INVALID_SOCKET;
- update_flags_notify(Fd::Flag::Read);
- return;
- }
-
- auto last_error = GetLastError();
- if (last_error == ERROR_IO_PENDING) {
- async_read_flag_ = true;
- return;
- }
- on_error(OS_SOCKET_ERROR("AcceptExFailed"), Fd::Flag::Read);
- }
-
- void loop() {
- CHECK(async_mode_);
-
- if (type_ == Fd::Type::EventFd) {
- return;
- }
- if (type_ == Fd::Type::ServerSocketFd) {
- while (async_read_flag_ == false && (internal_flags_ & Fd::Flag::Read) != 0) {
- // read always
- try_start_accept();
- }
- return;
- }
- if (!connected_) {
- return;
- }
- while (async_read_flag_ == false && (internal_flags_ & Fd::Flag::Read) != 0) {
- // read always
- try_start_read();
- }
- VLOG(fd) << (async_write_flag_ == false) << " " << output_reader_.size() << " "
- << ((internal_flags_ & Fd::Flag::Write) != 0);
- while (async_write_flag_ == false && output_reader_.size() && (internal_flags_ & Fd::Flag::Write) != 0) {
- // write if we have data to write
- try_start_write();
- }
- }
-};
-
-Fd::Fd() = default;
-
-Fd::Fd(Fd &&other) = default;
-
-Fd &Fd::operator=(Fd &&other) = default;
-
-Fd::~Fd() = default;
-
-Fd Fd::create_file_fd(HANDLE handle) {
- return Fd(Fd::Type::FileFd, Fd::Mode::Owner, handle);
-}
-
-Fd Fd::create_socket_fd(SOCKET sock) {
- return Fd(Fd::Type::SocketFd, Fd::Mode::Owner, sock, AF_UNSPEC);
-}
-
-Fd Fd::create_server_socket_fd(SOCKET sock, int socket_family) {
- return Fd(Fd::Type::ServerSocketFd, Fd::Mode::Owner, sock, socket_family);
-}
-
-Fd Fd::create_event_fd() {
- return Fd(Fd::Type::EventFd, Fd::Mode::Owner, INVALID_HANDLE_VALUE);
-}
-
-const Fd &Fd::get_fd() const {
- return *this;
-}
-
-Fd &Fd::get_fd() {
- return *this;
-}
-
-Result<size_t> Fd::read(MutableSlice slice) {
- return impl_->read(slice);
-}
-
-Result<size_t> Fd::write(Slice slice) {
- CHECK(!empty());
- return impl_->write(slice);
-}
-
-bool Fd::empty() const {
- return !impl_;
-}
-
-void Fd::close() {
- impl_.reset();
-}
-
-Result<Fd> Fd::accept() {
- return impl_->accept();
-}
-void Fd::connect(const IPAddress &addr) {
- return impl_->connect(addr);
-}
-
-Fd Fd::clone() const {
- return Fd(impl_);
-}
-
-uint64 Fd::get_key() const {
- return reinterpret_cast<uint64>(impl_.get());
-}
-
-void Fd::set_observer(ObserverBase *observer) {
- return impl_->set_observer(observer);
-}
-ObserverBase *Fd::get_observer() const {
- return impl_->get_observer();
-}
-
-Fd::Flags Fd::get_flags() const {
- return impl_->get_flags();
-}
-void Fd::update_flags(Flags flags) {
- impl_->update_flags(flags);
-}
-
-void Fd::on_read_event() {
- impl_->on_read_event();
-}
-void Fd::on_write_event() {
- impl_->on_write_event();
-}
-
-bool Fd::has_pending_error() const {
- return impl_->has_pending_error();
-}
-Status Fd::get_pending_error() {
- return impl_->get_pending_error();
-}
-
-HANDLE Fd::get_read_event() {
- return impl_->get_read_event();
-}
-HANDLE Fd::get_write_event() {
- return impl_->get_write_event();
-}
-
-SOCKET Fd::get_native_socket() const {
- return impl_->get_native_socket();
-}
-
-HANDLE Fd::get_io_handle() const {
- return impl_->get_io_handle();
-}
-
-#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
-Fd &Fd::Stderr() {
- static auto handle = GetStdHandle(STD_ERROR_HANDLE);
- LOG_IF(FATAL, handle == INVALID_HANDLE_VALUE) << "Failed to get stderr";
- static auto fd = Fd(Fd::Type::FileFd, Fd::Mode::Reference, handle);
- return fd;
-}
-Fd &Fd::Stdin() {
- static auto handle = GetStdHandle(STD_INPUT_HANDLE);
- LOG_IF(FATAL, handle == INVALID_HANDLE_VALUE) << "Failed to get stdin";
- static auto fd = Fd(Fd::Type::FileFd, Fd::Mode::Reference, handle);
- return fd;
-}
-Fd &Fd::Stdout() {
- static auto handle = GetStdHandle(STD_OUTPUT_HANDLE);
- LOG_IF(FATAL, handle == INVALID_HANDLE_VALUE) << "Failed to get stdout";
- static auto fd = Fd(Fd::Type::FileFd, Fd::Mode::Reference, handle);
- return fd;
-}
-#else
-Fd &Fd::Stderr() {
- static Fd result;
- result = Fd();
- return result;
-}
-Fd &Fd::Stdin() {
- static Fd result;
- result = Fd();
- return result;
-}
-Fd &Fd::Stdout() {
- static Fd result;
- result = Fd();
- return result;
-}
-#endif
-
-Status Fd::duplicate(const Fd &from, Fd &to) {
- return Status::Error("Not supported");
-}
-
-Status Fd::set_is_blocking(bool is_blocking) {
- return detail::set_native_socket_is_blocking(get_native_socket(), is_blocking);
-}
-
-Fd::Fd(Type type, Mode mode, HANDLE handle) : mode_(mode), impl_(std::make_shared<FdImpl>(type, handle)) {
-}
-
-Fd::Fd(Type type, Mode mode, SOCKET sock, int socket_family)
- : mode_(mode), impl_(std::make_shared<FdImpl>(type, sock, socket_family)) {
-}
-
-Fd::Fd(std::shared_ptr<FdImpl> impl) : mode_(Mode::Reference), impl_(std::move(impl)) {
-}
-
-void Fd::acquire() {
- return impl_->acquire();
-}
-
-void Fd::release() {
- return impl_->release();
-}
-
-class InitWSA {
- public:
- InitWSA() {
- /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
- WORD wVersionRequested = MAKEWORD(2, 2);
- WSADATA wsaData;
- if (WSAStartup(wVersionRequested, &wsaData) != 0) {
- auto error = OS_SOCKET_ERROR("Failed to init WSA");
- LOG(FATAL) << error;
- }
- }
-};
-
-static InitWSA init_wsa;
-
-#endif
-
-namespace detail {
-#if TD_PORT_POSIX
-Status set_native_socket_is_blocking(int fd, bool is_blocking) {
- if (fcntl(fd, F_SETFL, is_blocking ? 0 : O_NONBLOCK) == -1) {
-#elif TD_PORT_WINDOWS
-Status set_native_socket_is_blocking(SOCKET fd, bool is_blocking) {
- u_long mode = is_blocking;
- if (ioctlsocket(fd, FIONBIO, &mode) != 0) {
-#endif
- return OS_SOCKET_ERROR("Failed to change socket flags");
- }
- return Status::OK();
-}
-} // namespace detail
-
-} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.h
deleted file mode 100644
index 5ce9b6cedc..0000000000
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Fd.h
+++ /dev/null
@@ -1,226 +0,0 @@
-//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-#pragma once
-
-#include "td/utils/port/config.h"
-
-#include "td/utils/common.h"
-#include "td/utils/Slice.h"
-#include "td/utils/Status.h"
-
-#if TD_PORT_WINDOWS
-#include "td/utils/port/IPAddress.h"
-
-#include <memory>
-#endif
-
-#if TD_PORT_POSIX
-#include <errno.h>
-
-#include <atomic>
-#include <type_traits>
-#endif
-
-namespace td {
-
-class ObserverBase;
-
-#if TD_PORT_WINDOWS
-namespace detail {
-class EventFdWindows;
-} // namespace detail
-#endif
-
-class Fd {
- public:
- // TODO: Close may be not enough
- // Sometimes descriptor is half-closed.
-
- enum Flag : int32 {
- Write = 0x001,
- Read = 0x002,
- Close = 0x004,
- Error = 0x008,
- All = Write | Read | Close | Error,
- None = 0
- };
- using Flags = int32;
- enum class Mode { Reference, Owner };
-
- Fd();
- Fd(const Fd &) = delete;
- Fd &operator=(const Fd &) = delete;
- Fd(Fd &&other);
- Fd &operator=(Fd &&other);
- ~Fd();
-
-#if TD_PORT_POSIX
- Fd(int fd, Mode mode);
-#endif
-#if TD_PORT_WINDOWS
- static Fd create_file_fd(HANDLE handle);
-
- static Fd create_socket_fd(SOCKET sock);
-
- static Fd create_server_socket_fd(SOCKET sock, int socket_family);
-
- static Fd create_event_fd();
-#endif
-
- Fd clone() const;
-
- static Fd &Stderr();
- static Fd &Stdout();
- static Fd &Stdin();
-
- static Status duplicate(const Fd &from, Fd &to);
-
- bool empty() const;
-
- const Fd &get_fd() const;
- Fd &get_fd();
-
- void set_observer(ObserverBase *observer);
- ObserverBase *get_observer() const;
-
- void close();
-
- void update_flags(Flags flags);
-
- Flags get_flags() const;
-
- bool has_pending_error() const;
- Status get_pending_error() TD_WARN_UNUSED_RESULT;
-
- Result<size_t> write(Slice slice) TD_WARN_UNUSED_RESULT;
- Result<size_t> read(MutableSlice slice) TD_WARN_UNUSED_RESULT;
-
- Status set_is_blocking(bool is_blocking);
-
-#if TD_PORT_POSIX
- void update_flags_notify(Flags flags);
- void clear_flags(Flags flags);
-
- Result<size_t> write_unsafe(Slice slice) TD_WARN_UNUSED_RESULT;
-
- int get_native_fd() const;
- int move_as_native_fd();
-#endif
-
-#if TD_PORT_WINDOWS
- Result<Fd> accept() TD_WARN_UNUSED_RESULT;
- void connect(const IPAddress &addr);
-
- uint64 get_key() const;
-
- HANDLE get_read_event();
- HANDLE get_write_event();
- void on_read_event();
- void on_write_event();
-
- SOCKET get_native_socket() const;
- HANDLE get_io_handle() const;
-#endif
-
- private:
- Mode mode_ = Mode::Owner;
-
-#if TD_PORT_POSIX
- struct Info {
- std::atomic<int> refcnt;
- int32 flags;
- ObserverBase *observer;
- };
- struct InfoSet {
- InfoSet();
- Info &get_info(int32 id);
-
- private:
- static constexpr int MAX_FD = 1 << 18;
- Info fd_array_[MAX_FD];
- };
- static InfoSet fd_info_set_;
-
- static Fd stderr_;
- static Fd stdout_;
- static Fd stdin_;
-
- void update_flags_inner(int32 new_flags, bool notify_flag);
- Info *get_info();
- const Info *get_info() const;
- void clear_info();
-
- void close_ref();
- void close_own();
-
- int fd_ = -1;
-#endif
-#if TD_PORT_WINDOWS
- class FdImpl;
-
- enum class Type { Empty, EventFd, FileFd, StdinFileFd, SocketFd, ServerSocketFd };
-
- Fd(Type type, Mode mode, HANDLE handle);
- Fd(Type type, Mode mode, SOCKET sock, int socket_family);
- explicit Fd(std::shared_ptr<FdImpl> impl);
-
- friend class detail::EventFdWindows; // for release and acquire
-
- void acquire();
- void release();
-
- std::shared_ptr<FdImpl> impl_;
-#endif
-};
-
-#if TD_PORT_POSIX
-template <class F>
-auto skip_eintr(F &&f) {
- decltype(f()) res;
- static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
- do {
- errno = 0; // just in case
- res = f();
- } while (res < 0 && errno == EINTR);
- return res;
-}
-template <class F>
-auto skip_eintr_cstr(F &&f) {
- char *res;
- do {
- errno = 0; // just in case
- res = f();
- } while (res == nullptr && errno == EINTR);
- return res;
-}
-#endif
-
-template <class FdT>
-bool can_read(const FdT &fd) {
- return (fd.get_flags() & Fd::Read) != 0;
-}
-
-template <class FdT>
-bool can_write(const FdT &fd) {
- return (fd.get_flags() & Fd::Write) != 0;
-}
-
-template <class FdT>
-bool can_close(const FdT &fd) {
- return (fd.get_flags() & Fd::Close) != 0;
-}
-
-namespace detail {
-#if TD_PORT_POSIX
-Status set_native_socket_is_blocking(int fd, bool is_blocking);
-#endif
-#if TD_PORT_WINDOWS
-Status set_native_socket_is_blocking(SOCKET fd, bool is_blocking);
-#endif
-} // namespace detail
-
-} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.cpp
index 17a2727f64..6300f9f85f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,25 +7,42 @@
#include "td/utils/port/FileFd.h"
#if TD_PORT_WINDOWS
-#include "td/utils/misc.h" // for narrow_cast
-
+#include "td/utils/port/FromApp.h"
#include "td/utils/port/Stat.h"
#include "td/utils/port/wstring_convert.h"
#endif
+#include "td/utils/common.h"
+#include "td/utils/ExitGuard.h"
+#include "td/utils/FlatHashSet.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/detail/skip_eintr.h"
+#include "td/utils/port/PollFlags.h"
#include "td/utils/port/sleep.h"
+#include "td/utils/ScopeGuard.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/StringBuilder.h"
#include <cstring>
+#include <mutex>
+#include <utility>
#if TD_PORT_POSIX
+#include <cerrno>
+
#include <fcntl.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
+#else
+#include <limits>
+#endif
+
+#if TD_PORT_WINDOWS && defined(WIN32_LEAN_AND_MEAN)
+#include <winioctl.h>
#endif
namespace td {
@@ -38,8 +55,8 @@ struct PrintFlags {
StringBuilder &operator<<(StringBuilder &sb, const PrintFlags &print_flags) {
auto flags = print_flags.flags;
- if (flags &
- ~(FileFd::Write | FileFd::Read | FileFd::Truncate | FileFd::Create | FileFd::Append | FileFd::CreateNew)) {
+ if (flags & ~(FileFd::Write | FileFd::Read | FileFd::Truncate | FileFd::Create | FileFd::Append | FileFd::CreateNew |
+ FileFd::Direct | FileFd::WinStat)) {
return sb << "opened with invalid flags " << flags;
}
@@ -72,21 +89,34 @@ StringBuilder &operator<<(StringBuilder &sb, const PrintFlags &print_flags) {
if (flags & FileFd::Truncate) {
sb << " with truncation";
}
+ if (flags & FileFd::Direct) {
+ sb << " for direct io";
+ }
+ if (flags & FileFd::WinStat) {
+ sb << " for stat";
+ }
return sb;
}
} // namespace
-const Fd &FileFd::get_fd() const {
- return fd_;
-}
+namespace detail {
+class FileFdImpl {
+ public:
+ PollableFdInfo info_;
+};
+} // namespace detail
+
+FileFd::FileFd() = default;
+FileFd::FileFd(FileFd &&) noexcept = default;
+FileFd &FileFd::operator=(FileFd &&) noexcept = default;
+FileFd::~FileFd() = default;
-Fd &FileFd::get_fd() {
- return fd_;
+FileFd::FileFd(unique_ptr<detail::FileFdImpl> impl) : impl_(std::move(impl)) {
}
Result<FileFd> FileFd::open(CSlice filepath, int32 flags, int32 mode) {
- if (flags & ~(Write | Read | Truncate | Create | Append | CreateNew)) {
+ if (flags & ~(Write | Read | Truncate | Create | Append | CreateNew | Direct | WinStat)) {
return Status::Error(PSLICE() << "File \"" << filepath << "\" has failed to be " << PrintFlags{flags});
}
@@ -121,13 +151,17 @@ Result<FileFd> FileFd::open(CSlice filepath, int32 flags, int32 mode) {
native_flags |= O_APPEND;
}
- int native_fd = skip_eintr([&] { return ::open(filepath.c_str(), native_flags, static_cast<mode_t>(mode)); });
+#if TD_LINUX
+ if (flags & Direct) {
+ native_flags |= O_DIRECT;
+ }
+#endif
+
+ int native_fd = detail::skip_eintr([&] { return ::open(filepath.c_str(), native_flags, static_cast<mode_t>(mode)); });
if (native_fd < 0) {
return OS_ERROR(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags});
}
-
- FileFd result;
- result.fd_ = Fd(native_fd, Fd::Mode::Owner);
+ return from_native_fd(NativeFd(native_fd));
#elif TD_PORT_WINDOWS
// TODO: support modes
auto r_filepath = to_wstring(filepath);
@@ -165,165 +199,243 @@ Result<FileFd> FileFd::open(CSlice filepath, int32 flags, int32 mode) {
}
}
+ DWORD native_flags = 0;
+ if (flags & Direct) {
+ native_flags |= FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING;
+ }
+ if (flags & WinStat) {
+ native_flags |= FILE_FLAG_BACKUP_SEMANTICS;
+ }
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
- auto handle = CreateFile(w_filepath.c_str(), desired_access, share_mode, nullptr, creation_disposition, 0, nullptr);
+ auto handle =
+ CreateFile(w_filepath.c_str(), desired_access, share_mode, nullptr, creation_disposition, native_flags, nullptr);
#else
- auto handle = CreateFile2(w_filepath.c_str(), desired_access, share_mode, creation_disposition, nullptr);
+ CREATEFILE2_EXTENDED_PARAMETERS extended_parameters;
+ std::memset(&extended_parameters, 0, sizeof(extended_parameters));
+ extended_parameters.dwSize = sizeof(extended_parameters);
+ extended_parameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ extended_parameters.dwFileFlags = native_flags;
+ auto handle = td::CreateFile2FromAppW(w_filepath.c_str(), desired_access, share_mode, creation_disposition,
+ &extended_parameters);
#endif
if (handle == INVALID_HANDLE_VALUE) {
return OS_ERROR(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags});
}
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+ if (flags & Write) {
+ DWORD bytes_returned = 0;
+ DeviceIoControl(handle, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &bytes_returned, nullptr);
+ }
+#endif
+ auto native_fd = NativeFd(handle);
if (flags & Append) {
LARGE_INTEGER offset;
offset.QuadPart = 0;
auto set_pointer_res = SetFilePointerEx(handle, offset, nullptr, FILE_END);
if (!set_pointer_res) {
- auto res = OS_ERROR(PSLICE() << "Failed to seek to the end of file \"" << filepath << "\"");
- CloseHandle(handle);
- return res;
+ return OS_ERROR(PSLICE() << "Failed to seek to the end of file \"" << filepath << "\"");
}
}
- FileFd result;
- result.fd_ = Fd::create_file_fd(handle);
+ return from_native_fd(std::move(native_fd));
#endif
- result.fd_.update_flags(Fd::Flag::Write);
- return std::move(result);
+}
+
+FileFd FileFd::from_native_fd(NativeFd native_fd) {
+ auto impl = make_unique<detail::FileFdImpl>();
+ impl->info_.set_native_fd(std::move(native_fd));
+ impl->info_.add_flags(PollFlags::Write());
+ return FileFd(std::move(impl));
}
Result<size_t> FileFd::write(Slice slice) {
+ auto native_fd = get_native_fd().fd();
#if TD_PORT_POSIX
- CHECK(!fd_.empty());
- int native_fd = get_native_fd();
- auto write_res = skip_eintr([&] { return ::write(native_fd, slice.begin(), slice.size()); });
- if (write_res >= 0) {
- return narrow_cast<size_t>(write_res);
- }
-
- auto write_errno = errno;
- auto error = Status::PosixError(write_errno, PSLICE() << "Write to [fd = " << native_fd << "] has failed");
- if (write_errno != EAGAIN
-#if EAGAIN != EWOULDBLOCK
- && write_errno != EWOULDBLOCK
-#endif
- && write_errno != EIO) {
- LOG(ERROR) << error;
- }
- return std::move(error);
+ auto bytes_written = detail::skip_eintr([&] { return ::write(native_fd, slice.begin(), slice.size()); });
+ bool success = bytes_written >= 0;
#elif TD_PORT_WINDOWS
- return fd_.write(slice);
+ DWORD bytes_written = 0;
+ BOOL success = WriteFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_written, nullptr);
#endif
+ if (success) {
+ auto result = narrow_cast<size_t>(bytes_written);
+ CHECK(result <= slice.size());
+ return result;
+ }
+ return OS_ERROR(PSLICE() << "Write to " << get_native_fd() << " has failed");
}
-Result<size_t> FileFd::read(MutableSlice slice) {
+Result<size_t> FileFd::writev(Span<IoSlice> slices) {
#if TD_PORT_POSIX
- CHECK(!fd_.empty());
- int native_fd = get_native_fd();
- auto read_res = skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
- auto read_errno = errno;
-
- if (read_res >= 0) {
- if (narrow_cast<size_t>(read_res) < slice.size()) {
- fd_.clear_flags(Read);
+ auto native_fd = get_native_fd().fd();
+ TRY_RESULT(slices_size, narrow_cast_safe<int>(slices.size()));
+ auto bytes_written = detail::skip_eintr([&] { return ::writev(native_fd, slices.begin(), slices_size); });
+ bool success = bytes_written >= 0;
+ if (success) {
+ auto result = narrow_cast<size_t>(bytes_written);
+ auto left = result;
+ for (const auto &slice : slices) {
+ if (left <= slice.iov_len) {
+ return result;
+ }
+ left -= slice.iov_len;
+ }
+ UNREACHABLE();
+ }
+ return OS_ERROR(PSLICE() << "Writev to " << get_native_fd() << " has failed");
+#else
+ size_t res = 0;
+ for (const auto &slice : slices) {
+ if (slice.size() > std::numeric_limits<size_t>::max() - res) {
+ break;
+ }
+ TRY_RESULT(size, write(slice));
+ res += size;
+ if (size != slice.size()) {
+ CHECK(size < slice.size());
+ break;
}
- return static_cast<size_t>(read_res);
}
+ return res;
+#endif
+}
- auto error = Status::PosixError(read_errno, PSLICE() << "Read from [fd = " << native_fd << "] has failed");
- if (read_errno != EAGAIN
+Result<size_t> FileFd::read(MutableSlice slice) {
+ auto native_fd = get_native_fd().fd();
+#if TD_PORT_POSIX
+ auto bytes_read = detail::skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
+ bool success = bytes_read >= 0;
+ if (!success) {
+ auto read_errno = errno;
+ if (read_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
- && read_errno != EWOULDBLOCK
+ || read_errno == EWOULDBLOCK
#endif
- && read_errno != EIO) {
- LOG(ERROR) << error;
+ ) {
+ success = true;
+ bytes_read = 0;
+ }
}
- return std::move(error);
+ bool is_eof = success && narrow_cast<size_t>(bytes_read) < slice.size();
#elif TD_PORT_WINDOWS
- return fd_.read(slice);
+ DWORD bytes_read = 0;
+ BOOL success = ReadFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, nullptr);
+ bool is_eof = bytes_read == 0;
#endif
+ if (success) {
+ if (is_eof) {
+ get_poll_info().clear_flags(PollFlags::Read());
+ }
+ auto result = narrow_cast<size_t>(bytes_read);
+ CHECK(result <= slice.size());
+ return result;
+ }
+ return OS_ERROR(PSLICE() << "Read from " << get_native_fd() << " has failed");
}
Result<size_t> FileFd::pwrite(Slice slice, int64 offset) {
if (offset < 0) {
return Status::Error("Offset must be non-negative");
}
+ auto native_fd = get_native_fd().fd();
#if TD_PORT_POSIX
TRY_RESULT(offset_off_t, narrow_cast_safe<off_t>(offset));
- CHECK(!fd_.empty());
- int native_fd = get_native_fd();
- auto pwrite_res = skip_eintr([&] { return ::pwrite(native_fd, slice.begin(), slice.size(), offset_off_t); });
- if (pwrite_res >= 0) {
- return narrow_cast<size_t>(pwrite_res);
- }
-
- auto pwrite_errno = errno;
- auto error = Status::PosixError(
- pwrite_errno, PSLICE() << "Pwrite to [fd = " << native_fd << "] at [offset = " << offset << "] has failed");
- if (pwrite_errno != EAGAIN
-#if EAGAIN != EWOULDBLOCK
- && pwrite_errno != EWOULDBLOCK
-#endif
- && pwrite_errno != EIO) {
- LOG(ERROR) << error;
- }
- return std::move(error);
+ auto bytes_written =
+ detail::skip_eintr([&] { return ::pwrite(native_fd, slice.begin(), slice.size(), offset_off_t); });
+ bool success = bytes_written >= 0;
#elif TD_PORT_WINDOWS
DWORD bytes_written = 0;
OVERLAPPED overlapped;
std::memset(&overlapped, 0, sizeof(overlapped));
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
- auto res =
- WriteFile(fd_.get_io_handle(), slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_written, &overlapped);
- if (!res) {
- return OS_ERROR("Failed to pwrite");
- }
- return bytes_written;
+ BOOL success = WriteFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_written, &overlapped);
#endif
+ if (success) {
+ auto result = narrow_cast<size_t>(bytes_written);
+ CHECK(result <= slice.size());
+ return result;
+ }
+ return OS_ERROR(PSLICE() << "Pwrite to " << get_native_fd() << " at offset " << offset << " has failed");
}
-Result<size_t> FileFd::pread(MutableSlice slice, int64 offset) {
+Result<size_t> FileFd::pread(MutableSlice slice, int64 offset) const {
if (offset < 0) {
return Status::Error("Offset must be non-negative");
}
+ auto native_fd = get_native_fd().fd();
#if TD_PORT_POSIX
TRY_RESULT(offset_off_t, narrow_cast_safe<off_t>(offset));
- CHECK(!fd_.empty());
- int native_fd = get_native_fd();
- auto pread_res = skip_eintr([&] { return ::pread(native_fd, slice.begin(), slice.size(), offset_off_t); });
- if (pread_res >= 0) {
- return narrow_cast<size_t>(pread_res);
- }
-
- auto pread_errno = errno;
- auto error = Status::PosixError(
- pread_errno, PSLICE() << "Pread from [fd = " << native_fd << "] at [offset = " << offset << "] has failed");
- if (pread_errno != EAGAIN
-#if EAGAIN != EWOULDBLOCK
- && pread_errno != EWOULDBLOCK
-#endif
- && pread_errno != EIO) {
- LOG(ERROR) << error;
- }
- return std::move(error);
+ auto bytes_read = detail::skip_eintr([&] { return ::pread(native_fd, slice.begin(), slice.size(), offset_off_t); });
+ bool success = bytes_read >= 0;
#elif TD_PORT_WINDOWS
DWORD bytes_read = 0;
OVERLAPPED overlapped;
std::memset(&overlapped, 0, sizeof(overlapped));
overlapped.Offset = static_cast<DWORD>(offset);
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
- auto res = ReadFile(fd_.get_io_handle(), slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, &overlapped);
- if (!res) {
- return OS_ERROR("Failed to pread");
- }
- return bytes_read;
+ BOOL success = ReadFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, &overlapped);
#endif
+ if (success) {
+ auto result = narrow_cast<size_t>(bytes_read);
+ CHECK(result <= slice.size());
+ return result;
+ }
+ return OS_ERROR(PSLICE() << "Pread from " << get_native_fd() << " at offset " << offset << " has failed");
+}
+
+static std::mutex in_process_lock_mutex;
+static FlatHashSet<string> locked_files;
+static ExitGuard exit_guard;
+
+static Status create_local_lock(const string &path, int32 &max_tries) {
+ while (true) {
+ { // mutex lock scope
+ std::lock_guard<std::mutex> lock(in_process_lock_mutex);
+ if (!path.empty() && locked_files.count(path) == 0) {
+ VLOG(fd) << "Lock file \"" << path << '"';
+ locked_files.insert(path);
+ return Status::OK();
+ }
+ }
+
+ if (--max_tries <= 0) {
+ return Status::Error(
+ 0, PSLICE() << "Can't lock file \"" << path << "\", because it is already in use by current program");
+ }
+
+ usleep_for(100000);
+ }
}
-Status FileFd::lock(FileFd::LockFlags flags, int32 max_tries) {
+Status FileFd::lock(LockFlags flags, const string &path, int32 max_tries) {
if (max_tries <= 0) {
- return Status::Error(0, "Can't lock file: wrong max_tries");
+ return Status::Error("Can't lock file: wrong max_tries");
+ }
+
+ bool need_local_unlock = false;
+ if (!path.empty()) {
+ if (flags == LockFlags::Unlock) {
+ need_local_unlock = true;
+ } else if (flags == LockFlags::Read) {
+ LOG(FATAL) << "Local locking in Read mode is unsupported";
+ } else {
+ CHECK(flags == LockFlags::Write);
+ VLOG(fd) << "Trying to lock file \"" << path << '"';
+ TRY_STATUS(create_local_lock(path, max_tries));
+ need_local_unlock = true;
+ }
}
+ SCOPE_EXIT {
+ if (need_local_unlock) {
+ remove_local_lock(path);
+ }
+ };
+#if TD_PORT_POSIX
+ auto native_fd = get_native_fd().fd();
+#elif TD_PORT_WINDOWS
+ auto native_fd = get_native_fd().fd();
+#endif
while (true) {
#if TD_PORT_POSIX
struct flock lock;
@@ -344,95 +456,148 @@ Status FileFd::lock(FileFd::LockFlags flags, int32 max_tries) {
}());
lock.l_whence = SEEK_SET;
- if (fcntl(get_native_fd(), F_SETLK, &lock) == -1) {
- if (errno == EAGAIN && --max_tries > 0) {
+ if (fcntl(native_fd, F_SETLK, &lock) == -1) {
+ if (errno == EAGAIN) {
#elif TD_PORT_WINDOWS
OVERLAPPED overlapped;
std::memset(&overlapped, 0, sizeof(overlapped));
BOOL result;
if (flags == LockFlags::Unlock) {
- result = UnlockFileEx(fd_.get_io_handle(), 0, MAXDWORD, MAXDWORD, &overlapped);
+ result = UnlockFileEx(native_fd, 0, MAXDWORD, MAXDWORD, &overlapped);
} else {
DWORD dw_flags = LOCKFILE_FAIL_IMMEDIATELY;
if (flags == LockFlags::Write) {
dw_flags |= LOCKFILE_EXCLUSIVE_LOCK;
}
- result = LockFileEx(fd_.get_io_handle(), dw_flags, 0, MAXDWORD, MAXDWORD, &overlapped);
+ result = LockFileEx(native_fd, dw_flags, 0, MAXDWORD, MAXDWORD, &overlapped);
}
if (!result) {
- if (GetLastError() == ERROR_LOCK_VIOLATION && --max_tries > 0) {
+ if (GetLastError() == ERROR_LOCK_VIOLATION) {
#endif
- usleep_for(100000);
- continue;
+ if (--max_tries > 0) {
+ usleep_for(100000);
+ continue;
+ }
+
+ return OS_ERROR(PSLICE() << "Can't lock file \"" << path
+ << "\", because it is already in use; check for another program instance running");
}
return OS_ERROR("Can't lock file");
}
- return Status::OK();
+
+ break;
}
-}
-void FileFd::close() {
- fd_.close();
+ if (flags == LockFlags::Write) {
+ need_local_unlock = false;
+ }
+ return Status::OK();
}
-bool FileFd::empty() const {
- return fd_.empty();
+void FileFd::remove_local_lock(const string &path) {
+ if (path.empty() || ExitGuard::is_exited()) {
+ return;
+ }
+ VLOG(fd) << "Unlock file \"" << path << '"';
+ std::unique_lock<std::mutex> lock(in_process_lock_mutex);
+ auto erased_count = locked_files.erase(path);
+ CHECK(erased_count > 0 || path.empty() || ExitGuard::is_exited());
}
-#if TD_PORT_POSIX
-int FileFd::get_native_fd() const {
- return fd_.get_native_fd();
+void FileFd::close() {
+ impl_.reset();
}
-#endif
-int32 FileFd::get_flags() const {
- return fd_.get_flags();
+bool FileFd::empty() const {
+ return !impl_;
}
-void FileFd::update_flags(Fd::Flags mask) {
- fd_.update_flags(mask);
+const NativeFd &FileFd::get_native_fd() const {
+ return get_poll_info().native_fd();
}
-int64 FileFd::get_size() {
- return stat().size_;
+NativeFd FileFd::move_as_native_fd() {
+ auto res = get_poll_info().move_as_native_fd();
+ impl_.reset();
+ return res;
}
#if TD_PORT_WINDOWS
-static uint64 filetime_to_unix_time_nsec(LONGLONG filetime) {
+namespace {
+
+uint64 filetime_to_unix_time_nsec(LONGLONG filetime) {
const auto FILETIME_UNIX_TIME_DIFF = 116444736000000000ll;
return static_cast<uint64>((filetime - FILETIME_UNIX_TIME_DIFF) * 100);
}
+
+struct FileSize {
+ int64 size_;
+ int64 real_size_;
+};
+
+Result<FileSize> get_file_size(const FileFd &file_fd) {
+ FILE_STANDARD_INFO standard_info;
+ if (!GetFileInformationByHandleEx(file_fd.get_native_fd().fd(), FileStandardInfo, &standard_info,
+ sizeof(standard_info))) {
+ return OS_ERROR("Get FileStandardInfo failed");
+ }
+ FileSize res;
+ res.size_ = standard_info.EndOfFile.QuadPart;
+ res.real_size_ = standard_info.AllocationSize.QuadPart;
+
+ if (res.size_ > 0 && res.real_size_ <= 0) { // just in case
+ LOG(ERROR) << "Fix real file size from " << res.real_size_ << " to " << res.size_;
+ res.real_size_ = res.size_;
+ }
+
+ return res;
+}
+
+} // namespace
+#endif
+
+Result<int64> FileFd::get_size() const {
+#if TD_PORT_POSIX
+ TRY_RESULT(s, stat());
+#elif TD_PORT_WINDOWS
+ TRY_RESULT(s, get_file_size(*this));
#endif
+ return s.size_;
+}
-Stat FileFd::stat() {
+Result<int64> FileFd::get_real_size() const {
+#if TD_PORT_POSIX
+ TRY_RESULT(s, stat());
+#elif TD_PORT_WINDOWS
+ TRY_RESULT(s, get_file_size(*this));
+#endif
+ return s.real_size_;
+}
+
+Result<Stat> FileFd::stat() const {
CHECK(!empty());
#if TD_PORT_POSIX
- return detail::fstat(get_native_fd());
+ return detail::fstat(get_native_fd().fd());
#elif TD_PORT_WINDOWS
Stat res;
FILE_BASIC_INFO basic_info;
- auto status = GetFileInformationByHandleEx(fd_.get_io_handle(), FileBasicInfo, &basic_info, sizeof(basic_info));
+ auto status = GetFileInformationByHandleEx(get_native_fd().fd(), FileBasicInfo, &basic_info, sizeof(basic_info));
if (!status) {
- auto error = OS_ERROR("Stat failed");
- LOG(FATAL) << error;
+ return OS_ERROR("Get FileBasicInfo failed");
}
res.atime_nsec_ = filetime_to_unix_time_nsec(basic_info.LastAccessTime.QuadPart);
res.mtime_nsec_ = filetime_to_unix_time_nsec(basic_info.LastWriteTime.QuadPart);
res.is_dir_ = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- res.is_reg_ = true;
+ res.is_reg_ = !res.is_dir_; // TODO this is still wrong
- FILE_STANDARD_INFO standard_info;
- status = GetFileInformationByHandleEx(fd_.get_io_handle(), FileStandardInfo, &standard_info, sizeof(standard_info));
- if (!status) {
- auto error = OS_ERROR("Stat failed");
- LOG(FATAL) << error;
- }
- res.size_ = standard_info.EndOfFile.QuadPart;
+ TRY_RESULT(file_size, get_file_size(*this));
+ res.size_ = file_size.size_;
+ res.real_size_ = file_size.real_size_;
return res;
#endif
@@ -441,9 +606,13 @@ Stat FileFd::stat() {
Status FileFd::sync() {
CHECK(!empty());
#if TD_PORT_POSIX
- if (fsync(fd_.get_native_fd()) != 0) {
+#if TD_DARWIN
+ if (detail::skip_eintr([&] { return fcntl(get_native_fd().fd(), F_FULLFSYNC); }) == -1) {
+#else
+ if (detail::skip_eintr([&] { return fsync(get_native_fd().fd()); }) != 0) {
+#endif
#elif TD_PORT_WINDOWS
- if (FlushFileBuffers(fd_.get_io_handle()) == 0) {
+ if (FlushFileBuffers(get_native_fd().fd()) == 0) {
#endif
return OS_ERROR("Sync failed");
}
@@ -454,11 +623,11 @@ Status FileFd::seek(int64 position) {
CHECK(!empty());
#if TD_PORT_POSIX
TRY_RESULT(position_off_t, narrow_cast_safe<off_t>(position));
- if (skip_eintr([&] { return ::lseek(fd_.get_native_fd(), position_off_t, SEEK_SET); }) < 0) {
+ if (detail::skip_eintr([&] { return ::lseek(get_native_fd().fd(), position_off_t, SEEK_SET); }) < 0) {
#elif TD_PORT_WINDOWS
LARGE_INTEGER offset;
offset.QuadPart = position;
- if (SetFilePointerEx(fd_.get_io_handle(), offset, nullptr, FILE_BEGIN) == 0) {
+ if (SetFilePointerEx(get_native_fd().fd(), offset, nullptr, FILE_BEGIN) == 0) {
#endif
return OS_ERROR("Seek failed");
}
@@ -469,13 +638,21 @@ Status FileFd::truncate_to_current_position(int64 current_position) {
CHECK(!empty());
#if TD_PORT_POSIX
TRY_RESULT(current_position_off_t, narrow_cast_safe<off_t>(current_position));
- if (skip_eintr([&] { return ::ftruncate(fd_.get_native_fd(), current_position_off_t); }) < 0) {
+ if (detail::skip_eintr([&] { return ::ftruncate(get_native_fd().fd(), current_position_off_t); }) < 0) {
#elif TD_PORT_WINDOWS
- if (SetEndOfFile(fd_.get_io_handle()) == 0) {
+ if (SetEndOfFile(get_native_fd().fd()) == 0) {
#endif
return OS_ERROR("Truncate failed");
}
return Status::OK();
}
+PollableFdInfo &FileFd::get_poll_info() {
+ CHECK(!empty());
+ return impl_->info_;
+}
+const PollableFdInfo &FileFd::get_poll_info() const {
+ CHECK(!empty());
+ return impl_->info_;
+}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.h
index bf7166c1de..3dc88da5cb 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FileFd.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,42 +9,57 @@
#include "td/utils/port/config.h"
#include "td/utils/common.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/IoSlice.h"
#include "td/utils/port/Stat.h"
#include "td/utils/Slice.h"
+#include "td/utils/Span.h"
#include "td/utils/Status.h"
namespace td {
+namespace detail {
+class FileFdImpl;
+} // namespace detail
class FileFd {
public:
- FileFd() = default;
+ FileFd();
+ FileFd(FileFd &&) noexcept;
+ FileFd &operator=(FileFd &&) noexcept;
+ ~FileFd();
+ FileFd(const FileFd &) = delete;
+ FileFd &operator=(const FileFd &) = delete;
- enum Flags : int32 { Write = 1, Read = 2, Truncate = 4, Create = 8, Append = 16, CreateNew = 32 };
-
- const Fd &get_fd() const;
- Fd &get_fd();
+ enum Flags : int32 { Write = 1, Read = 2, Truncate = 4, Create = 8, Append = 16, CreateNew = 32, Direct = 64 };
+ enum PrivateFlags : int32 { WinStat = 128 };
static Result<FileFd> open(CSlice filepath, int32 flags, int32 mode = 0600) TD_WARN_UNUSED_RESULT;
+ static FileFd from_native_fd(NativeFd fd) TD_WARN_UNUSED_RESULT;
Result<size_t> write(Slice slice) TD_WARN_UNUSED_RESULT;
+ Result<size_t> writev(Span<IoSlice> slices) TD_WARN_UNUSED_RESULT;
Result<size_t> read(MutableSlice slice) TD_WARN_UNUSED_RESULT;
Result<size_t> pwrite(Slice slice, int64 offset) TD_WARN_UNUSED_RESULT;
- Result<size_t> pread(MutableSlice slice, int64 offset) TD_WARN_UNUSED_RESULT;
+ Result<size_t> pread(MutableSlice slice, int64 offset) const TD_WARN_UNUSED_RESULT;
enum class LockFlags { Write, Read, Unlock };
- Status lock(LockFlags flags, int32 max_tries = 1) TD_WARN_UNUSED_RESULT;
+ Status lock(LockFlags flags, const string &path, int32 max_tries) TD_WARN_UNUSED_RESULT;
+ static void remove_local_lock(const string &path);
+
+ PollableFdInfo &get_poll_info();
+ const PollableFdInfo &get_poll_info() const;
void close();
+
bool empty() const;
- int32 get_flags() const;
- void update_flags(Fd::Flags mask);
+ Result<int64> get_size() const;
- int64 get_size();
+ Result<int64> get_real_size() const;
- Stat stat();
+ Result<Stat> stat() const;
Status sync() TD_WARN_UNUSED_RESULT;
@@ -52,12 +67,13 @@ class FileFd {
Status truncate_to_current_position(int64 current_position) TD_WARN_UNUSED_RESULT;
-#if TD_PORT_POSIX
- int get_native_fd() const;
-#endif
+ const NativeFd &get_native_fd() const;
+ NativeFd move_as_native_fd();
private:
- Fd fd_;
+ unique_ptr<detail::FileFdImpl> impl_;
+
+ explicit FileFd(unique_ptr<detail::FileFdImpl> impl);
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FromApp.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FromApp.h
new file mode 100644
index 0000000000..b33cbf070c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/FromApp.h
@@ -0,0 +1,100 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+
+#ifdef TD_PORT_WINDOWS
+
+namespace td {
+
+#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+inline HMODULE GetKernelModule() {
+ static const auto kernel_module = []() -> HMODULE {
+ MEMORY_BASIC_INFORMATION mbi;
+ if (VirtualQuery(VirtualQuery, &mbi, sizeof(MEMORY_BASIC_INFORMATION))) {
+ return reinterpret_cast<HMODULE>(mbi.AllocationBase);
+ }
+ return nullptr;
+ }();
+ return kernel_module;
+}
+
+inline HMODULE LoadLibrary(LPCTSTR lpFileName) {
+ using pLoadLibrary = HMODULE(WINAPI *)(_In_ LPCTSTR);
+ static const auto proc_load_library =
+ reinterpret_cast<pLoadLibrary>(GetProcAddress(GetKernelModule(), "LoadLibraryW"));
+ return proc_load_library(lpFileName);
+}
+
+inline HMODULE GetFromAppModule() {
+ static const HMODULE from_app_module = LoadLibrary(L"api-ms-win-core-file-fromapp-l1-1-0.dll");
+ return from_app_module;
+}
+#endif
+
+template <int num, class T>
+T *get_from_app_function(const char *name, T *original_func) {
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+ return original_func;
+#else
+ static T *func = [name, original_func]() -> T * {
+ auto func_pointer = GetProcAddress(GetFromAppModule(), name);
+ if (func_pointer == nullptr) {
+ return original_func;
+ }
+ return reinterpret_cast<T *>(func_pointer);
+ }();
+ return func;
+#endif
+}
+
+inline HANDLE CreateFile2FromAppW(_In_ LPCWSTR lpFileName, _In_ DWORD dwDesiredAccess, _In_ DWORD dwShareMode,
+ _In_ DWORD dwCreationDisposition,
+ _In_opt_ LPCREATEFILE2_EXTENDED_PARAMETERS pCreateExParams) {
+ auto func = get_from_app_function<0>("CreateFile2FromAppW", &CreateFile2);
+ return func(lpFileName, dwDesiredAccess, dwShareMode, dwCreationDisposition, pCreateExParams);
+}
+
+inline BOOL CreateDirectoryFromAppW(_In_ LPCWSTR lpPathName, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes) {
+ auto func = get_from_app_function<1>("CreateDirectoryFromAppW", &CreateDirectory);
+ return func(lpPathName, lpSecurityAttributes);
+}
+
+inline BOOL RemoveDirectoryFromAppW(_In_ LPCWSTR lpPathName) {
+ auto func = get_from_app_function<2>("RemoveDirectoryFromAppW", &RemoveDirectory);
+ return func(lpPathName);
+}
+
+inline BOOL DeleteFileFromAppW(_In_ LPCWSTR lpFileName) {
+ auto func = get_from_app_function<3>("DeleteFileFromAppW", &DeleteFile);
+ return func(lpFileName);
+}
+
+inline BOOL MoveFileExFromAppW(_In_ LPCWSTR lpExistingFileName, _In_ LPCWSTR lpNewFileName, _In_ DWORD dwFlags) {
+ auto func = get_from_app_function<4>("MoveFileFromAppW", static_cast<BOOL(WINAPI *)(LPCWSTR, LPCWSTR)>(nullptr));
+ if (func == nullptr || (dwFlags & ~MOVEFILE_REPLACE_EXISTING) != 0) {
+ // if can't find MoveFileFromAppW or have unsupported flags, call MoveFileEx directly
+ return MoveFileEx(lpExistingFileName, lpNewFileName, dwFlags);
+ }
+ if ((dwFlags & MOVEFILE_REPLACE_EXISTING) != 0) {
+ td::DeleteFileFromAppW(lpNewFileName);
+ }
+ return func(lpExistingFileName, lpNewFileName);
+}
+
+inline HANDLE FindFirstFileExFromAppW(_In_ LPCWSTR lpFileName, _In_ FINDEX_INFO_LEVELS fInfoLevelId,
+ _Out_writes_bytes_(sizeof(WIN32_FIND_DATAW)) LPVOID lpFindFileData,
+ _In_ FINDEX_SEARCH_OPS fSearchOp, _Reserved_ LPVOID lpSearchFilter,
+ _In_ DWORD dwAdditionalFlags) {
+ auto func = get_from_app_function<5>("FindFirstFileExFromAppW", &FindFirstFileEx);
+ return func(lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags);
+}
+
+} // namespace td
+
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.cpp
index 2d3a3cdbc0..32c26403d5 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.cpp
@@ -1,9 +1,11 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
+#define _WINSOCK_DEPRECATED_NO_WARNINGS // we need to use inet_addr instead of inet_pton
+
#include "td/utils/port/IPAddress.h"
#include "td/utils/format.h"
@@ -12,19 +14,185 @@
#include "td/utils/port/SocketFd.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/ScopeGuard.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/utf8.h"
-#if !TD_WINDOWS
+#if TD_WINDOWS
+#include "td/utils/port/wstring_convert.h"
+#else
#include <netdb.h>
-#include <netinet/in.h>
#include <netinet/tcp.h>
-#include <sys/socket.h>
#include <sys/types.h>
#endif
#include <cstring>
+#include <limits>
namespace td {
+static bool is_ascii_host_char(char c) {
+ return static_cast<unsigned char>(c) <= 127;
+}
+
+static bool is_ascii_host(Slice host) {
+ for (auto c : host) {
+ if (!is_ascii_host_char(c)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+#if !TD_WINDOWS
+static void punycode(string &result, Slice part) {
+ vector<uint32> codes;
+ codes.reserve(utf8_length(part));
+ uint32 processed = 0;
+ auto begin = part.ubegin();
+ auto end = part.uend();
+ while (begin != end) {
+ uint32 code;
+ begin = next_utf8_unsafe(begin, &code);
+ if (code <= 127u) {
+ result += to_lower(static_cast<char>(code));
+ processed++;
+ }
+ codes.push_back(code);
+ }
+
+ if (processed > 0) {
+ result += '-';
+ }
+
+ uint32 n = 127;
+ uint32 delta = 0;
+ int bias = -72;
+ bool is_first = true;
+ while (processed < codes.size()) {
+ // choose lowest not processed code
+ uint32 next_n = 0x110000;
+ for (auto code : codes) {
+ if (code > n && code < next_n) {
+ next_n = code;
+ }
+ }
+ delta += (next_n - n - 1) * (processed + 1);
+
+ for (auto code : codes) {
+ if (code < next_n) {
+ delta++;
+ }
+
+ if (code == next_n) {
+ // found next symbol, encode delta
+ auto left = static_cast<int>(delta);
+ while (true) {
+ bias += 36;
+ auto t = clamp(bias, 1, 26);
+ if (left < t) {
+ result += static_cast<char>(left + 'a');
+ break;
+ }
+
+ left -= t;
+ auto digit = t + left % (36 - t);
+ result += static_cast<char>(digit < 26 ? digit + 'a' : digit - 26 + '0');
+ left /= 36 - t;
+ }
+ processed++;
+
+ // update bias
+ if (is_first) {
+ delta /= 700;
+ is_first = false;
+ } else {
+ delta /= 2;
+ }
+ delta += delta / processed;
+
+ bias = 0;
+ while (delta > 35 * 13) {
+ delta /= 35;
+ bias -= 36;
+ }
+ bias -= static_cast<int>(36 * delta / (delta + 38));
+ delta = 0;
+ }
+ }
+
+ delta++;
+ n = next_n;
+ }
+}
+#endif
+
+Result<string> idn_to_ascii(CSlice host) {
+ if (is_ascii_host(host)) {
+ return to_lower(host);
+ }
+ if (!check_utf8(host)) {
+ return Status::Error("Host name must be encoded in UTF-8");
+ }
+
+ const int MAX_DNS_NAME_LENGTH = 255;
+ if (host.size() >= MAX_DNS_NAME_LENGTH * 4) { // upper bound, 4 characters per symbol
+ return Status::Error("Host name is too long");
+ }
+
+#if TD_WINDOWS
+ TRY_RESULT(whost, to_wstring(host));
+ wchar_t punycode[MAX_DNS_NAME_LENGTH + 1];
+ int result_length =
+ IdnToAscii(IDN_ALLOW_UNASSIGNED, whost.c_str(), narrow_cast<int>(whost.size()), punycode, MAX_DNS_NAME_LENGTH);
+ if (result_length == 0) {
+ return Status::Error("Host can't be converted to ASCII");
+ }
+
+ TRY_RESULT(idn_host, from_wstring(punycode, result_length));
+ return idn_host;
+#else
+ auto parts = full_split(Slice(host), '.');
+ bool is_first = true;
+ string result;
+ result.reserve(host.size());
+ for (auto part : parts) {
+ if (!is_first) {
+ result += '.';
+ }
+ if (is_ascii_host(part)) {
+ result.append(part.data(), part.size());
+ } else {
+ // TODO nameprep should be applied first, but punycode is better than nothing.
+ // It is better to use libidn/ICU here if available
+ result += "xn--";
+ punycode(result, part);
+ }
+ is_first = false;
+ }
+ return result;
+#endif
+}
+
+static CSlice get_ip_str(int family, const void *addr) {
+ const int buf_size = INET6_ADDRSTRLEN;
+ static TD_THREAD_LOCAL char *buf;
+ init_thread_local<char[]>(buf, buf_size);
+
+ const char *res = inet_ntop(family,
+#if TD_WINDOWS
+ const_cast<PVOID>(addr),
+#else
+ addr,
+#endif
+ buf, buf_size);
+ if (res == nullptr) {
+ return CSlice();
+ } else {
+ return CSlice(res);
+ }
+}
+
IPAddress::IPAddress() : is_valid_(false) {
}
@@ -32,19 +200,52 @@ bool IPAddress::is_valid() const {
return is_valid_;
}
+bool IPAddress::is_reserved() const {
+ CHECK(is_valid());
+
+ if (is_ipv6()) {
+ // TODO proper check for reserved IPv6 addresses
+ return true;
+ }
+
+ uint32 ip = get_ipv4();
+ struct IpBlock {
+ CSlice ip;
+ int mask;
+ IpBlock(CSlice ip, int mask) : ip(ip), mask(mask) {
+ }
+ };
+ static const IpBlock blocks[] = {{"0.0.0.0", 8}, {"10.0.0.0", 8}, {"100.64.0.0", 10}, {"127.0.0.0", 8},
+ {"169.254.0.0", 16}, {"172.16.0.0", 12}, {"192.0.0.0", 24}, {"192.0.2.0", 24},
+ {"192.88.99.0", 24}, {"192.168.0.0", 16}, {"198.18.0.0", 15}, {"198.51.100.0", 24},
+ {"203.0.113.0", 24}, {"224.0.0.0", 3}};
+ for (auto &block : blocks) {
+ IPAddress block_ip_address;
+ block_ip_address.init_ipv4_port(block.ip, 80).ensure();
+ uint32 range = block_ip_address.get_ipv4();
+ CHECK(block.mask != 0);
+ uint32 mask = std::numeric_limits<uint32>::max() >> (32 - block.mask) << (32 - block.mask);
+ if ((ip & mask) == (range & mask)) {
+ return true;
+ }
+ }
+ return false;
+}
+
const sockaddr *IPAddress::get_sockaddr() const {
+ CHECK(is_valid());
return &sockaddr_;
}
size_t IPAddress::get_sockaddr_len() const {
CHECK(is_valid());
- switch (addr_.ss_family) {
+ switch (sockaddr_.sa_family) {
case AF_INET6:
return sizeof(ipv6_addr_);
case AF_INET:
return sizeof(ipv4_addr_);
default:
- LOG(FATAL) << "Unknown address family";
+ UNREACHABLE();
return 0;
}
}
@@ -54,20 +255,24 @@ int IPAddress::get_address_family() const {
}
bool IPAddress::is_ipv4() const {
- return get_address_family() == AF_INET;
+ return is_valid() && get_address_family() == AF_INET;
+}
+
+bool IPAddress::is_ipv6() const {
+ return is_valid() && get_address_family() == AF_INET6;
}
uint32 IPAddress::get_ipv4() const {
CHECK(is_valid());
CHECK(is_ipv4());
- return ipv4_addr_.sin_addr.s_addr;
+ return htonl(ipv4_addr_.sin_addr.s_addr);
}
-Slice IPAddress::get_ipv6() const {
+string IPAddress::get_ipv6() const {
static_assert(sizeof(ipv6_addr_.sin6_addr) == 16, "ipv6 size == 16");
CHECK(is_valid());
CHECK(!is_ipv4());
- return Slice(ipv6_addr_.sin6_addr.s6_addr, 16);
+ return Slice(ipv6_addr_.sin6_addr.s6_addr, 16).str();
}
IPAddress IPAddress::get_any_addr() const {
@@ -80,18 +285,23 @@ IPAddress IPAddress::get_any_addr() const {
res.init_ipv4_any();
break;
default:
- LOG(FATAL) << "Unknown address family";
+ UNREACHABLE();
+ break;
}
return res;
}
+
void IPAddress::init_ipv4_any() {
is_valid_ = true;
+ std::memset(&ipv4_addr_, 0, sizeof(ipv4_addr_));
ipv4_addr_.sin_family = AF_INET;
ipv4_addr_.sin_addr.s_addr = INADDR_ANY;
ipv4_addr_.sin_port = 0;
}
+
void IPAddress::init_ipv6_any() {
is_valid_ = true;
+ std::memset(&ipv6_addr_, 0, sizeof(ipv6_addr_));
ipv6_addr_.sin6_family = AF_INET6;
ipv6_addr_.sin6_addr = in6addr_any;
ipv6_addr_.sin6_port = 0;
@@ -100,7 +310,12 @@ void IPAddress::init_ipv6_any() {
Status IPAddress::init_ipv6_port(CSlice ipv6, int port) {
is_valid_ = false;
if (port <= 0 || port >= (1 << 16)) {
- return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
+ return Status::Error(PSLICE() << "Invalid [IPv6 address port=" << port << "]");
+ }
+ string ipv6_plain;
+ if (ipv6.size() > 2 && ipv6[0] == '[' && ipv6.back() == ']') {
+ ipv6_plain.assign(ipv6.begin() + 1, ipv6.size() - 2);
+ ipv6 = ipv6_plain;
}
std::memset(&ipv6_addr_, 0, sizeof(ipv6_addr_));
ipv6_addr_.sin6_family = AF_INET6;
@@ -122,7 +337,7 @@ Status IPAddress::init_ipv6_as_ipv4_port(CSlice ipv4, int port) {
Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
is_valid_ = false;
if (port <= 0 || port >= (1 << 16)) {
- return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
+ return Status::Error(PSLICE() << "Invalid [IPv4 address port=" << port << "]");
}
std::memset(&ipv4_addr_, 0, sizeof(ipv4_addr_));
ipv4_addr_.sin_family = AF_INET;
@@ -137,36 +352,116 @@ Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
return Status::OK();
}
-Status IPAddress::init_host_port(CSlice host, int port) {
- auto str_port = to_string(port);
- return init_host_port(host, str_port);
+Result<IPAddress> IPAddress::get_ip_address(CSlice host) {
+ auto r_address = get_ipv4_address(host);
+ if (r_address.is_ok()) {
+ return r_address.move_as_ok();
+ }
+ r_address = get_ipv6_address(host);
+ if (r_address.is_ok()) {
+ return r_address.move_as_ok();
+ }
+ return Status::Error(PSLICE() << '"' << host << "\" is not a valid IP address");
+}
+
+Result<IPAddress> IPAddress::get_ipv4_address(CSlice host) {
+ // sometimes inet_addr allows much more valid IPv4 hosts than inet_pton,
+ // like 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001
+ auto ipv4_numeric_addr = inet_addr(host.c_str());
+ if (ipv4_numeric_addr == INADDR_NONE) {
+ return Status::Error(PSLICE() << '"' << host << "\" is not a valid IPv4 address");
+ }
+
+ host = ::td::get_ip_str(AF_INET, &ipv4_numeric_addr);
+ IPAddress result;
+ auto status = result.init_ipv4_port(host, 1);
+ if (status.is_error()) {
+ return std::move(status);
+ }
+ return std::move(result);
+}
+
+Result<IPAddress> IPAddress::get_ipv6_address(CSlice host) {
+ IPAddress result;
+ auto status = result.init_ipv6_port(host, 1);
+ if (status.is_error()) {
+ return Status::Error(PSLICE() << '"' << host << "\" is not a valid IPv6 address");
+ }
+ return std::move(result);
+}
+
+Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) {
+ if (host.size() > 2 && host[0] == '[' && host.back() == ']') {
+ return init_ipv6_port(host, port == 0 ? 1 : port);
+ }
+
+ return init_host_port(host, PSLICE() << port, prefer_ipv6);
}
-Status IPAddress::init_host_port(CSlice host, CSlice port) {
+Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
+ is_valid_ = false;
+ if (host.empty()) {
+ return Status::Error("Host is empty");
+ }
+#if TD_WINDOWS
+ if (host == "..localmachine") {
+ return Status::Error("Host is invalid");
+ }
+#endif
+ TRY_RESULT(ascii_host, idn_to_ascii(host));
+ host = ascii_host; // assign string to CSlice
+
+ if (host[0] == '[' && host.back() == ']') {
+ auto port_int = to_integer<int>(port);
+ return init_ipv6_port(host, port_int == 0 ? 1 : port_int);
+ }
+
+ // some getaddrinfo implementations use inet_pton instead of inet_aton and support only decimal-dotted IPv4 form,
+ // and so doesn't recognize 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001 as valid IPv4 addresses
+ auto ipv4_numeric_addr = inet_addr(host.c_str());
+ if (ipv4_numeric_addr != INADDR_NONE) {
+ host = ::td::get_ip_str(AF_INET, &ipv4_numeric_addr);
+ }
+
addrinfo hints;
addrinfo *info = nullptr;
std::memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET; // TODO AF_UNSPEC;
+ hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- LOG(INFO) << "Try to init IP address of " << host << " with port " << port;
- auto s = getaddrinfo(host.c_str(), port.c_str(), &hints, &info);
- if (s != 0) {
- return Status::Error(PSLICE() << "getaddrinfo: " << gai_strerror(s));
+ hints.ai_protocol = IPPROTO_TCP;
+ LOG(DEBUG + 10) << "Trying to init IP address of " << host << " with port " << port;
+ auto err = getaddrinfo(host.c_str(), port.c_str(), &hints, &info);
+ if (err != 0) {
+#if TD_WINDOWS
+ return OS_SOCKET_ERROR("Failed to resolve host");
+#else
+ return Status::Error(PSLICE() << "Failed to resolve host: " << gai_strerror(err));
+#endif
}
SCOPE_EXIT {
freeaddrinfo(info);
};
- // prefer ipv4
- addrinfo *best_info = info;
- for (auto *ptr = info->ai_next; ptr != nullptr; ptr = ptr->ai_next) {
- if (ptr->ai_socktype == AF_INET) {
+ addrinfo *best_info = nullptr;
+ for (auto *ptr = info; ptr != nullptr; ptr = ptr->ai_next) {
+ if (ptr->ai_family == AF_INET && (!prefer_ipv6 || best_info == nullptr)) {
+ // just use first IPv4 address if there is no IPv6 and it isn't preferred
best_info = ptr;
- break;
+ if (!prefer_ipv6) {
+ break;
+ }
}
+ if (ptr->ai_family == AF_INET6 && (prefer_ipv6 || best_info == nullptr)) {
+ // or first IPv6 address if there is no IPv4 and it isn't preferred
+ best_info = ptr;
+ if (prefer_ipv6) {
+ break;
+ }
+ }
+ }
+ if (best_info == nullptr) {
+ return Status::Error("Failed to find IPv4/IPv6 address");
}
- // just use first address
- CHECK(best_info != nullptr);
return init_sockaddr(best_info->ai_addr, narrow_cast<socklen_t>(best_info->ai_addrlen));
}
@@ -178,32 +473,39 @@ Status IPAddress::init_host_port(CSlice host_port) {
return init_host_port(host_port.substr(0, pos).str(), host_port.substr(pos + 1).str());
}
+Status IPAddress::init_sockaddr(sockaddr *addr) {
+ if (addr->sa_family == AF_INET6) {
+ return init_sockaddr(addr, sizeof(ipv6_addr_));
+ } else if (addr->sa_family == AF_INET) {
+ return init_sockaddr(addr, sizeof(ipv4_addr_));
+ } else {
+ return init_sockaddr(addr, 0);
+ }
+}
Status IPAddress::init_sockaddr(sockaddr *addr, socklen_t len) {
if (addr->sa_family == AF_INET6) {
CHECK(len == sizeof(ipv6_addr_));
std::memcpy(&ipv6_addr_, reinterpret_cast<sockaddr_in6 *>(addr), sizeof(ipv6_addr_));
- LOG(INFO) << "Have ipv6 address " << get_ip_str() << " with port " << get_port();
} else if (addr->sa_family == AF_INET) {
CHECK(len == sizeof(ipv4_addr_));
std::memcpy(&ipv4_addr_, reinterpret_cast<sockaddr_in *>(addr), sizeof(ipv4_addr_));
- LOG(INFO) << "Have ipv4 address " << get_ip_str() << " with port " << get_port();
} else {
return Status::Error(PSLICE() << "Unknown " << tag("sa_family", addr->sa_family));
}
is_valid_ = true;
+ LOG(DEBUG + 10) << "Have address " << get_ip_str() << " with port " << get_port();
return Status::OK();
}
Status IPAddress::init_socket_address(const SocketFd &socket_fd) {
is_valid_ = false;
-#if TD_WINDOWS
- auto fd = socket_fd.get_fd().get_native_socket();
-#else
- auto fd = socket_fd.get_fd().get_native_fd();
-#endif
- socklen_t len = sizeof(addr_);
- int ret = getsockname(fd, &sockaddr_, &len);
+ if (socket_fd.empty()) {
+ return Status::Error("Socket is empty");
+ }
+ auto socket = socket_fd.get_native_fd().socket();
+ socklen_t len = storage_size();
+ int ret = getsockname(socket, &sockaddr_, &len);
if (ret != 0) {
return OS_SOCKET_ERROR("Failed to get socket address");
}
@@ -213,13 +515,12 @@ Status IPAddress::init_socket_address(const SocketFd &socket_fd) {
Status IPAddress::init_peer_address(const SocketFd &socket_fd) {
is_valid_ = false;
-#if TD_WINDOWS
- auto fd = socket_fd.get_fd().get_native_socket();
-#else
- auto fd = socket_fd.get_fd().get_native_fd();
-#endif
- socklen_t len = sizeof(addr_);
- int ret = getpeername(fd, &sockaddr_, &len);
+ if (socket_fd.empty()) {
+ return Status::Error("Socket is empty");
+ }
+ auto socket = socket_fd.get_native_fd().socket();
+ socklen_t len = storage_size();
+ int ret = getpeername(socket, &sockaddr_, &len);
if (ret != 0) {
return OS_SOCKET_ERROR("Failed to get peer socket address");
}
@@ -227,48 +528,57 @@ Status IPAddress::init_peer_address(const SocketFd &socket_fd) {
return Status::OK();
}
-static CSlice get_ip_str(int family, const void *addr) {
- const int buf_size = INET6_ADDRSTRLEN; //, INET_ADDRSTRLEN;
- static TD_THREAD_LOCAL char *buf;
- init_thread_local<char[]>(buf, buf_size);
-
- const char *res = inet_ntop(family,
-#if TD_WINDOWS
- const_cast<PVOID>(addr),
-#else
- addr,
-#endif
- buf, buf_size);
- if (res == nullptr) {
- return CSlice();
- } else {
- return CSlice(res);
+void IPAddress::clear_ipv6_interface() {
+ if (!is_valid() || get_address_family() != AF_INET6) {
+ return;
}
+
+ auto *begin = ipv6_addr_.sin6_addr.s6_addr;
+ static_assert(sizeof(ipv6_addr_.sin6_addr.s6_addr) == 16, "expected 16 bytes buffer for ipv6");
+ static_assert(sizeof(*begin) == 1, "expected array of bytes");
+ std::memset(begin + 8, 0, 8 * sizeof(*begin));
}
-CSlice IPAddress::ipv4_to_str(int32 ipv4) {
- auto tmp_ipv4 = ntohl(ipv4);
- return ::td::get_ip_str(AF_INET, &tmp_ipv4);
+string IPAddress::ipv4_to_str(uint32 ipv4) {
+ ipv4 = ntohl(ipv4);
+ return ::td::get_ip_str(AF_INET, &ipv4).str();
}
-Slice IPAddress::get_ip_str() const {
+string IPAddress::ipv6_to_str(Slice ipv6) {
+ CHECK(ipv6.size() == 16);
+ return ::td::get_ip_str(AF_INET6, ipv6.ubegin()).str();
+}
+
+CSlice IPAddress::get_ip_str() const {
if (!is_valid()) {
- return Slice("0.0.0.0");
+ return CSlice("0.0.0.0");
}
- const void *addr;
switch (get_address_family()) {
case AF_INET6:
- addr = &ipv6_addr_.sin6_addr;
- break;
+ return ::td::get_ip_str(AF_INET6, &ipv6_addr_.sin6_addr);
case AF_INET:
- addr = &ipv4_addr_.sin_addr;
- break;
+ return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr);
default:
UNREACHABLE();
- return Slice();
+ return CSlice();
+ }
+}
+
+string IPAddress::get_ip_host() const {
+ if (!is_valid()) {
+ return "0.0.0.0";
+ }
+
+ switch (get_address_family()) {
+ case AF_INET6:
+ return PSTRING() << '[' << ::td::get_ip_str(AF_INET6, &ipv6_addr_.sin6_addr) << ']';
+ case AF_INET:
+ return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr).str();
+ default:
+ UNREACHABLE();
+ return string();
}
- return ::td::get_ip_str(get_address_family(), addr);
}
int IPAddress::get_port() const {
@@ -304,7 +614,7 @@ void IPAddress::set_port(int port) {
bool operator==(const IPAddress &a, const IPAddress &b) {
if (!a.is_valid() || !b.is_valid()) {
- return false;
+ return !a.is_valid() && !b.is_valid();
}
if (a.get_address_family() != b.get_address_family()) {
return false;
@@ -318,13 +628,13 @@ bool operator==(const IPAddress &a, const IPAddress &b) {
std::memcmp(&a.ipv6_addr_.sin6_addr, &b.ipv6_addr_.sin6_addr, sizeof(a.ipv6_addr_.sin6_addr)) == 0;
}
- LOG(FATAL) << "Unknown address family";
+ UNREACHABLE();
return false;
}
bool operator<(const IPAddress &a, const IPAddress &b) {
- if (a.is_valid() != b.is_valid()) {
- return a.is_valid() < b.is_valid();
+ if (!a.is_valid() || !b.is_valid()) {
+ return !a.is_valid() && b.is_valid();
}
if (a.get_address_family() != b.get_address_family()) {
return a.get_address_family() < b.get_address_family();
@@ -342,7 +652,7 @@ bool operator<(const IPAddress &a, const IPAddress &b) {
return std::memcmp(&a.ipv6_addr_.sin6_addr, &b.ipv6_addr_.sin6_addr, sizeof(a.ipv6_addr_.sin6_addr)) < 0;
}
- LOG(FATAL) << "Unknown address family";
+ UNREACHABLE();
return false;
}
@@ -350,12 +660,7 @@ StringBuilder &operator<<(StringBuilder &builder, const IPAddress &address) {
if (!address.is_valid()) {
return builder << "[invalid]";
}
- if (address.get_address_family() == AF_INET) {
- return builder << "[" << address.get_ip_str() << ":" << address.get_port() << "]";
- } else {
- CHECK(address.get_address_family() == AF_INET6);
- return builder << "[[" << address.get_ip_str() << "]:" << address.get_port() << "]";
- }
+ return builder << "[" << address.get_ip_host() << ":" << address.get_port() << "]";
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.h
index 116a4c5425..f71bc5da91 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IPAddress.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,53 +15,79 @@
#if !TD_WINDOWS
#include <arpa/inet.h>
+#include <netinet/in.h>
#include <sys/socket.h>
#endif
namespace td {
+
+Result<string> idn_to_ascii(CSlice host);
+
class SocketFd;
+
class IPAddress {
public:
IPAddress();
bool is_valid() const;
-
- const sockaddr *get_sockaddr() const;
- size_t get_sockaddr_len() const;
- int get_address_family() const;
- Slice get_ip_str() const;
bool is_ipv4() const;
- uint32 get_ipv4() const;
- Slice get_ipv6() const;
+ bool is_ipv6() const;
+
+ bool is_reserved() const;
+
int get_port() const;
void set_port(int port);
+ uint32 get_ipv4() const;
+ string get_ipv6() const;
+
+ // returns result in a static thread-local buffer, which may be overwritten by any subsequent method call
+ CSlice get_ip_str() const;
+
+ // returns IP address as a host, i.e. IPv4 or [IPv6]
+ string get_ip_host() const;
+
+ static string ipv4_to_str(uint32 ipv4);
+ static string ipv6_to_str(Slice ipv6);
+
IPAddress get_any_addr() const;
+ static Result<IPAddress> get_ip_address(CSlice host); // host must be any IPv4 or IPv6
+ static Result<IPAddress> get_ipv4_address(CSlice host);
+ static Result<IPAddress> get_ipv6_address(CSlice host);
+
Status init_ipv6_port(CSlice ipv6, int port) TD_WARN_UNUSED_RESULT;
Status init_ipv6_as_ipv4_port(CSlice ipv4, int port) TD_WARN_UNUSED_RESULT;
Status init_ipv4_port(CSlice ipv4, int port) TD_WARN_UNUSED_RESULT;
- Status init_host_port(CSlice host, int port) TD_WARN_UNUSED_RESULT;
- Status init_host_port(CSlice host, CSlice port) TD_WARN_UNUSED_RESULT;
+ Status init_host_port(CSlice host, int port, bool prefer_ipv6 = false) TD_WARN_UNUSED_RESULT;
+ Status init_host_port(CSlice host, CSlice port, bool prefer_ipv6 = false) TD_WARN_UNUSED_RESULT;
Status init_host_port(CSlice host_port) TD_WARN_UNUSED_RESULT;
Status init_socket_address(const SocketFd &socket_fd) TD_WARN_UNUSED_RESULT;
Status init_peer_address(const SocketFd &socket_fd) TD_WARN_UNUSED_RESULT;
+ void clear_ipv6_interface();
+
friend bool operator==(const IPAddress &a, const IPAddress &b);
friend bool operator<(const IPAddress &a, const IPAddress &b);
- static CSlice ipv4_to_str(int32 ipv4);
+ // for internal usage only
+ const sockaddr *get_sockaddr() const;
+ size_t get_sockaddr_len() const;
+ int get_address_family() const;
+ Status init_sockaddr(sockaddr *addr);
+ Status init_sockaddr(sockaddr *addr, socklen_t len) TD_WARN_UNUSED_RESULT;
private:
union {
- sockaddr_storage addr_;
sockaddr sockaddr_;
sockaddr_in ipv4_addr_;
sockaddr_in6 ipv6_addr_;
};
+ static constexpr socklen_t storage_size() {
+ return sizeof(ipv6_addr_);
+ }
bool is_valid_;
- Status init_sockaddr(sockaddr *addr, socklen_t len) TD_WARN_UNUSED_RESULT;
void init_ipv4_any();
void init_ipv6_any();
};
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IoSlice.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IoSlice.h
new file mode 100644
index 0000000000..5044fcec37
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/IoSlice.h
@@ -0,0 +1,42 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/Slice.h"
+
+#if TD_PORT_POSIX
+#include <sys/uio.h>
+#endif
+
+namespace td {
+
+#if TD_PORT_POSIX
+
+using IoSlice = struct iovec;
+
+inline IoSlice as_io_slice(Slice slice) {
+ IoSlice res;
+ res.iov_len = slice.size();
+ res.iov_base = const_cast<char *>(slice.data());
+ return res;
+}
+
+inline Slice as_slice(const IoSlice &io_slice) {
+ return Slice(static_cast<const char *>(io_slice.iov_base), io_slice.iov_len);
+}
+
+#else
+
+using IoSlice = Slice;
+
+inline IoSlice as_io_slice(Slice slice) {
+ return slice;
+}
+
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.cpp
new file mode 100644
index 0000000000..0781b609b3
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.cpp
@@ -0,0 +1,109 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/MemoryMapping.h"
+
+#include "td/utils/misc.h"
+#include "td/utils/SliceBuilder.h"
+
+// TODO:
+// windows,
+// anonymous maps,
+// huge pages?
+
+#if TD_WINDOWS
+#else
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
+namespace td {
+
+class MemoryMapping::Impl {
+ public:
+ Impl(MutableSlice data, int64 offset) : data_(data), offset_(offset) {
+ }
+ Slice as_slice() const {
+ return data_.substr(narrow_cast<size_t>(offset_));
+ }
+ MutableSlice as_mutable_slice() const {
+ return {};
+ }
+
+ private:
+ MutableSlice data_;
+ int64 offset_;
+};
+
+#if !TD_WINDOWS
+static Result<int64> get_page_size() {
+ static Result<int64> page_size = []() -> Result<int64> {
+ auto page_size = sysconf(_SC_PAGESIZE);
+ if (page_size < 0) {
+ return OS_ERROR("Can't load page size from sysconf");
+ }
+ return page_size;
+ }();
+ return page_size.clone();
+}
+#endif
+
+Result<MemoryMapping> MemoryMapping::create_anonymous(const MemoryMapping::Options &options) {
+ return Status::Error("Unsupported yet");
+}
+
+Result<MemoryMapping> MemoryMapping::create_from_file(const FileFd &file_fd, const MemoryMapping::Options &options) {
+#if TD_WINDOWS
+ return Status::Error("Unsupported yet");
+#else
+ if (file_fd.empty()) {
+ return Status::Error("Can't create memory mapping: file is empty");
+ }
+ TRY_RESULT(stat, file_fd.stat());
+ auto fd = file_fd.get_native_fd().fd();
+ auto begin = options.offset;
+ if (begin < 0) {
+ return Status::Error(PSLICE() << "Can't create memory mapping: negative offset " << options.offset);
+ }
+
+ int64 end;
+ if (options.size < 0) {
+ end = stat.size_;
+ } else {
+ end = begin + stat.size_;
+ }
+
+ TRY_RESULT(page_size, get_page_size());
+ auto fixed_begin = begin / page_size * page_size;
+
+ auto data_offset = begin - fixed_begin;
+ TRY_RESULT(data_size, narrow_cast_safe<size_t>(end - fixed_begin));
+
+ void *data = mmap(nullptr, data_size, PROT_READ, MAP_PRIVATE, fd, narrow_cast<off_t>(fixed_begin));
+ if (data == MAP_FAILED) {
+ return OS_ERROR("mmap call failed");
+ }
+
+ return MemoryMapping(make_unique<Impl>(MutableSlice(static_cast<char *>(data), data_size), data_offset));
+#endif
+}
+
+MemoryMapping::MemoryMapping(MemoryMapping &&other) noexcept = default;
+MemoryMapping &MemoryMapping::operator=(MemoryMapping &&other) noexcept = default;
+MemoryMapping::~MemoryMapping() = default;
+
+MemoryMapping::MemoryMapping(unique_ptr<Impl> impl) : impl_(std::move(impl)) {
+}
+
+Slice MemoryMapping::as_slice() const {
+ return impl_->as_slice();
+}
+
+MutableSlice MemoryMapping::as_mutable_slice() {
+ return impl_->as_mutable_slice();
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.h
new file mode 100644
index 0000000000..040e45f989
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/MemoryMapping.h
@@ -0,0 +1,52 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/port/FileFd.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+
+namespace td {
+
+class MemoryMapping {
+ public:
+ struct Options {
+ int64 offset{0};
+ int64 size{-1};
+
+ Options() {
+ }
+ Options &with_offset(int64 new_offset) {
+ offset = new_offset;
+ return *this;
+ }
+ Options &with_size(int64 new_size) {
+ size = new_size;
+ return *this;
+ }
+ };
+
+ static Result<MemoryMapping> create_anonymous(const Options &options = {});
+ static Result<MemoryMapping> create_from_file(const FileFd &file, const Options &options = {});
+
+ Slice as_slice() const;
+ MutableSlice as_mutable_slice(); // returns empty slice if memory is read-only
+
+ MemoryMapping(const MemoryMapping &other) = delete;
+ const MemoryMapping &operator=(const MemoryMapping &other) = delete;
+ MemoryMapping(MemoryMapping &&other) noexcept;
+ MemoryMapping &operator=(MemoryMapping &&other) noexcept;
+ ~MemoryMapping();
+
+ private:
+ class Impl;
+ unique_ptr<Impl> impl_;
+ explicit MemoryMapping(unique_ptr<Impl> impl);
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Mutex.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Mutex.h
new file mode 100644
index 0000000000..40d9f482db
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Mutex.h
@@ -0,0 +1,30 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include <mutex>
+
+namespace td {
+
+class Mutex {
+ public:
+ struct Guard {
+ std::unique_lock<std::mutex> guard;
+ void reset() {
+ guard.unlock();
+ }
+ };
+
+ Guard lock() {
+ return {std::unique_lock<std::mutex>(mutex_)};
+ }
+
+ private:
+ std::mutex mutex_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Poll.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Poll.h
index e23f4382d0..92629d6693 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Poll.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Poll.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollBase.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollBase.h
index eb71367ab9..674f8da677 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollBase.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollBase.h
@@ -1,12 +1,13 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/PollFlags.h"
namespace td {
class PollBase {
@@ -19,9 +20,9 @@ class PollBase {
virtual ~PollBase() = default;
virtual void init() = 0;
virtual void clear() = 0;
- virtual void subscribe(const Fd &fd, Fd::Flags flags) = 0;
- virtual void unsubscribe(const Fd &fd) = 0;
- virtual void unsubscribe_before_close(const Fd &fd) = 0;
+ virtual void subscribe(PollableFd fd, PollFlags flags) = 0;
+ virtual void unsubscribe(PollableFdRef fd) = 0;
+ virtual void unsubscribe_before_close(PollableFdRef fd) = 0;
virtual void run(int timeout_ms) = 0;
};
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.cpp
new file mode 100644
index 0000000000..d729dcfa1e
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.cpp
@@ -0,0 +1,71 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/PollFlags.h"
+
+namespace td {
+
+bool PollFlagsSet::write_flags(PollFlags flags) {
+ if (flags.empty()) {
+ return false;
+ }
+ auto old_flags = to_write_.fetch_or(flags.raw(), std::memory_order_relaxed);
+ return (flags.raw() & ~old_flags) != 0;
+}
+
+bool PollFlagsSet::write_flags_local(PollFlags flags) {
+ return flags_.add_flags(flags);
+}
+
+bool PollFlagsSet::flush() const {
+ if (to_write_.load(std::memory_order_relaxed) == 0) {
+ return false;
+ }
+ auto to_write = to_write_.exchange(0, std::memory_order_relaxed);
+ auto old_flags = flags_;
+ flags_.add_flags(PollFlags::from_raw(to_write));
+ if (flags_.can_close()) {
+ flags_.remove_flags(PollFlags::Write());
+ }
+ return flags_ != old_flags;
+}
+
+PollFlags PollFlagsSet::read_flags() const {
+ flush();
+ return flags_;
+}
+
+PollFlags PollFlagsSet::read_flags_local() const {
+ return flags_;
+}
+
+void PollFlagsSet::clear_flags(PollFlags flags) {
+ flags_.remove_flags(flags);
+}
+
+void PollFlagsSet::clear() {
+ to_write_ = 0;
+ flags_ = {};
+}
+
+StringBuilder &operator<<(StringBuilder &sb, PollFlags flags) {
+ sb << '[';
+ if (flags.can_read()) {
+ sb << 'R';
+ }
+ if (flags.can_write()) {
+ sb << 'W';
+ }
+ if (flags.can_close()) {
+ sb << 'C';
+ }
+ if (flags.has_pending_error()) {
+ sb << 'E';
+ }
+ return sb << ']';
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.h
new file mode 100644
index 0000000000..f759cb44cb
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/PollFlags.h
@@ -0,0 +1,122 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/StringBuilder.h"
+
+#include <atomic>
+
+namespace td {
+
+class PollFlags {
+ public:
+ using Raw = int32;
+ bool can_read() const {
+ return has_flags(Read());
+ }
+ bool can_write() const {
+ return has_flags(Write());
+ }
+ bool can_close() const {
+ return has_flags(Close());
+ }
+ bool has_pending_error() const {
+ return has_flags(Error());
+ }
+ void remove_flags(PollFlags flags) {
+ remove_flags(flags.raw());
+ }
+ bool add_flags(PollFlags flags) {
+ auto old_flags = flags_;
+ add_flags(flags.raw());
+ return old_flags != flags_;
+ }
+ bool has_flags(PollFlags flags) const {
+ return has_flags(flags.raw());
+ }
+
+ bool empty() const {
+ return flags_ == 0;
+ }
+ Raw raw() const {
+ return flags_;
+ }
+ static PollFlags from_raw(Raw raw) {
+ return PollFlags(raw);
+ }
+ PollFlags() = default;
+
+ bool operator==(const PollFlags &other) const {
+ return flags_ == other.flags_;
+ }
+ bool operator!=(const PollFlags &other) const {
+ return !(*this == other);
+ }
+ PollFlags operator|(const PollFlags other) const {
+ return from_raw(raw() | other.raw());
+ }
+
+ static PollFlags Write() {
+ return PollFlags(Flag::Write);
+ }
+ static PollFlags Error() {
+ return PollFlags(Flag::Error);
+ }
+ static PollFlags Close() {
+ return PollFlags(Flag::Close);
+ }
+ static PollFlags Read() {
+ return PollFlags(Flag::Read);
+ }
+ static PollFlags ReadWrite() {
+ return Read() | Write();
+ }
+
+ private:
+ enum class Flag : Raw { Write = 0x001, Read = 0x002, Close = 0x004, Error = 0x008, None = 0 };
+ Raw flags_{static_cast<Raw>(Flag::None)};
+
+ explicit PollFlags(Raw raw) : flags_(raw) {
+ }
+ explicit PollFlags(Flag flag) : PollFlags(static_cast<Raw>(flag)) {
+ }
+
+ PollFlags &add_flags(Raw flags) {
+ flags_ |= flags;
+ return *this;
+ }
+ PollFlags &remove_flags(Raw flags) {
+ flags_ &= ~flags;
+ return *this;
+ }
+ bool has_flags(Raw flags) const {
+ return (flags_ & flags) == flags;
+ }
+};
+
+StringBuilder &operator<<(StringBuilder &sb, PollFlags flags);
+
+class PollFlagsSet {
+ public:
+ // write flags from any thread
+ // this is the only function that should be called from other threads
+ bool write_flags(PollFlags flags);
+
+ bool write_flags_local(PollFlags flags);
+ bool flush() const;
+
+ PollFlags read_flags() const;
+ PollFlags read_flags_local() const;
+ void clear_flags(PollFlags flags);
+ void clear();
+
+ private:
+ mutable std::atomic<PollFlags::Raw> to_write_{0};
+ mutable PollFlags flags_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/RwMutex.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/RwMutex.h
index eee5f3dcdb..01de499bd0 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/RwMutex.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/RwMutex.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,14 +9,16 @@
#include "td/utils/port/config.h"
#include "td/utils/common.h"
-#include "td/utils/logging.h"
#include "td/utils/Status.h"
#if TD_PORT_POSIX
#include <pthread.h>
#endif
+#include <memory>
+
namespace td {
+
class RwMutex {
public:
RwMutex() {
@@ -24,11 +26,11 @@ class RwMutex {
}
RwMutex(const RwMutex &) = delete;
RwMutex &operator=(const RwMutex &) = delete;
- RwMutex(RwMutex &&other) {
+ RwMutex(RwMutex &&other) noexcept {
init();
other.clear();
}
- RwMutex &operator=(RwMutex &&other) {
+ RwMutex &operator=(RwMutex &&other) noexcept {
other.clear();
return *this;
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp
index ead43a3d4b..139252b405 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,10 +8,15 @@
#include "td/utils/port/config.h"
+#include "td/utils/common.h"
#include "td/utils/logging.h"
+#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/port/IPAddress.h"
+#include "td/utils/port/PollFlags.h"
+#include "td/utils/SliceBuilder.h"
#if TD_PORT_POSIX
+#include <cerrno>
#include <arpa/inet.h>
#include <fcntl.h>
@@ -20,141 +25,348 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
+#endif
+#if TD_PORT_WINDOWS
+#include "td/utils/port/detail/Iocp.h"
+#include "td/utils/port/Mutex.h"
+#include "td/utils/VectorQueue.h"
#endif
+#include <atomic>
+#include <cstring>
+
namespace td {
-Result<ServerSocketFd> ServerSocketFd::open(int32 port, CSlice addr) {
- ServerSocketFd socket;
- TRY_STATUS(socket.init(port, addr));
- return std::move(socket);
-}
+namespace detail {
+#if TD_PORT_WINDOWS
+class ServerSocketFdImpl final : private Iocp::Callback {
+ public:
+ ServerSocketFdImpl(NativeFd fd, int socket_family) : info_(std::move(fd)), socket_family_(socket_family) {
+ VLOG(fd) << get_native_fd() << " create ServerSocketFd";
+ Iocp::get()->subscribe(get_native_fd(), this);
+ notify_iocp_read();
+ }
+ void close() {
+ notify_iocp_close();
+ }
+ PollableFdInfo &get_poll_info() {
+ return info_;
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return info_;
+ }
-const Fd &ServerSocketFd::get_fd() const {
- return fd_;
-}
+ const NativeFd &get_native_fd() const {
+ return info_.native_fd();
+ }
-Fd &ServerSocketFd::get_fd() {
- return fd_;
-}
+ Result<SocketFd> accept() {
+ auto lock = lock_.lock();
+ if (accepted_.empty()) {
+ get_poll_info().clear_flags(PollFlags::Read());
+ return Status::Error(-1, "Operation would block");
+ }
+ return accepted_.pop();
+ }
-int32 ServerSocketFd::get_flags() const {
- return fd_.get_flags();
-}
+ Status get_pending_error() {
+ Status res;
+ {
+ auto lock = lock_.lock();
+ if (!pending_errors_.empty()) {
+ res = pending_errors_.pop();
+ }
+ if (res.is_ok()) {
+ get_poll_info().clear_flags(PollFlags::Error());
+ }
+ }
+ return res;
+ }
-Status ServerSocketFd::get_pending_error() {
- return fd_.get_pending_error();
+ private:
+ PollableFdInfo info_;
+
+ Mutex lock_;
+ VectorQueue<SocketFd> accepted_;
+ VectorQueue<Status> pending_errors_;
+ static constexpr size_t MAX_ADDR_SIZE = sizeof(sockaddr_in6) + 16;
+ char addr_buf_[MAX_ADDR_SIZE * 2];
+
+ bool close_flag_{false};
+ std::atomic<int> refcnt_{1};
+ bool is_read_active_{false};
+ WSAOVERLAPPED read_overlapped_;
+
+ char close_overlapped_;
+
+ NativeFd accept_socket_;
+ int socket_family_;
+
+ void on_close() {
+ close_flag_ = true;
+ info_.set_native_fd({});
+ }
+ void on_read() {
+ VLOG(fd) << get_native_fd() << " on_read";
+ if (is_read_active_) {
+ is_read_active_ = false;
+ auto r_socket = [&]() -> Result<SocketFd> {
+ auto from = get_native_fd().socket();
+ auto status = setsockopt(accept_socket_.socket(), SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,
+ reinterpret_cast<const char *>(&from), sizeof(from));
+ if (status != 0) {
+ return OS_SOCKET_ERROR("Failed to set SO_UPDATE_ACCEPT_CONTEXT options");
+ }
+ return SocketFd::from_native_fd(std::move(accept_socket_));
+ }();
+ VLOG(fd) << get_native_fd() << " finish accept";
+ if (r_socket.is_error()) {
+ return on_error(r_socket.move_as_error());
+ }
+ {
+ auto lock = lock_.lock();
+ accepted_.push(r_socket.move_as_ok());
+ }
+ get_poll_info().add_flags_from_poll(PollFlags::Read());
+ }
+ loop_read();
+ }
+ void loop_read() {
+ CHECK(!is_read_active_);
+ accept_socket_ = NativeFd(socket(socket_family_, SOCK_STREAM, 0));
+ std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
+ VLOG(fd) << get_native_fd() << " start accept";
+ BOOL status = AcceptEx(get_native_fd().socket(), accept_socket_.socket(), addr_buf_, 0, MAX_ADDR_SIZE,
+ MAX_ADDR_SIZE, nullptr, &read_overlapped_);
+ if (status == TRUE || check_status("Failed to accept connection")) {
+ inc_refcnt();
+ is_read_active_ = true;
+ }
+ }
+ bool check_status(Slice message) {
+ auto last_error = WSAGetLastError();
+ if (last_error == ERROR_IO_PENDING) {
+ return true;
+ }
+ on_error(OS_SOCKET_ERROR(message));
+ return false;
+ }
+ bool dec_refcnt() {
+ if (--refcnt_ == 0) {
+ delete this;
+ return true;
+ }
+ return false;
+ }
+ void inc_refcnt() {
+ CHECK(refcnt_ != 0);
+ refcnt_++;
+ }
+
+ void on_error(Status status) {
+ {
+ auto lock = lock_.lock();
+ pending_errors_.push(std::move(status));
+ }
+ get_poll_info().add_flags_from_poll(PollFlags::Error());
+ }
+
+ void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) final {
+ // called from other thread
+ if (dec_refcnt() || close_flag_) {
+ VLOG(fd) << "Ignore IOCP (server socket is closing)";
+ return;
+ }
+ if (r_size.is_error()) {
+ return on_error(get_socket_pending_error(get_native_fd(), overlapped, r_size.move_as_error()));
+ }
+
+ if (overlapped == nullptr) {
+ return on_read();
+ }
+ if (overlapped == &read_overlapped_) {
+ return on_read();
+ }
+
+ if (overlapped == reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_)) {
+ return on_close();
+ }
+ UNREACHABLE();
+ }
+ void notify_iocp_read() {
+ VLOG(fd) << get_native_fd() << " notify_read";
+ inc_refcnt();
+ Iocp::get()->post(0, this, nullptr);
+ }
+ void notify_iocp_close() {
+ VLOG(fd) << get_native_fd() << " notify_close";
+ Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_));
+ }
+};
+void ServerSocketFdImplDeleter::operator()(ServerSocketFdImpl *impl) {
+ impl->close();
}
+#elif TD_PORT_POSIX
+class ServerSocketFdImpl {
+ public:
+ explicit ServerSocketFdImpl(NativeFd fd) : info_(std::move(fd)) {
+ }
+ PollableFdInfo &get_poll_info() {
+ return info_;
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return info_;
+ }
-Result<SocketFd> ServerSocketFd::accept() {
-#if TD_PORT_POSIX
- sockaddr_storage addr;
- socklen_t addr_len = sizeof(addr);
- int native_fd = fd_.get_native_fd();
- int r_fd = skip_eintr([&] { return ::accept(native_fd, reinterpret_cast<sockaddr *>(&addr), &addr_len); });
- auto accept_errno = errno;
- if (r_fd >= 0) {
- return SocketFd::from_native_fd(r_fd);
+ const NativeFd &get_native_fd() const {
+ return info_.native_fd();
}
+ Result<SocketFd> accept() {
+ sockaddr_storage addr;
+ socklen_t addr_len = sizeof(addr);
+ int native_fd = get_native_fd().socket();
+ int r_fd = detail::skip_eintr([&] { return ::accept(native_fd, reinterpret_cast<sockaddr *>(&addr), &addr_len); });
+ auto accept_errno = errno;
+ if (r_fd >= 0) {
+ return SocketFd::from_native_fd(NativeFd(r_fd));
+ }
- if (accept_errno == EAGAIN
+ if (accept_errno == EAGAIN
#if EAGAIN != EWOULDBLOCK
- || accept_errno == EWOULDBLOCK
+ || accept_errno == EWOULDBLOCK
#endif
- ) {
- fd_.clear_flags(Fd::Read);
- return Status::Error(-1, "Operation would block");
- }
-
- auto error = Status::PosixError(accept_errno, PSLICE() << "Accept from [fd = " << native_fd << "] has failed");
- switch (accept_errno) {
- case EBADF:
- case EFAULT:
- case EINVAL:
- case ENOTSOCK:
- case EOPNOTSUPP:
- LOG(FATAL) << error;
- UNREACHABLE();
- break;
- default:
- LOG(ERROR) << error;
- // fallthrough
- case EMFILE:
- case ENFILE:
- case ECONNABORTED: //???
- fd_.clear_flags(Fd::Read);
- fd_.update_flags(Fd::Close);
- return std::move(error);
+ ) {
+ get_poll_info().clear_flags(PollFlags::Read());
+ return Status::Error(-1, "Operation would block");
+ }
+
+ auto error = Status::PosixError(accept_errno, PSLICE() << "Accept from " << get_native_fd() << " has failed");
+ switch (accept_errno) {
+ case EBADF:
+ case EFAULT:
+ case EINVAL:
+ case ENOTSOCK:
+ case EOPNOTSUPP:
+ LOG(FATAL) << error;
+ UNREACHABLE();
+ break;
+ default:
+ LOG(ERROR) << error;
+ // fallthrough
+ case EMFILE:
+ case ENFILE:
+ case ECONNABORTED: //???
+ get_poll_info().clear_flags(PollFlags::Read());
+ get_poll_info().add_flags(PollFlags::Close());
+ return std::move(error);
+ }
}
-#elif TD_PORT_WINDOWS
- TRY_RESULT(socket_fd, fd_.accept());
- return SocketFd(std::move(socket_fd));
+
+ Status get_pending_error() {
+ if (!get_poll_info().get_flags_local().has_pending_error()) {
+ return Status::OK();
+ }
+ TRY_STATUS(detail::get_socket_pending_error(get_native_fd()));
+ get_poll_info().clear_flags(PollFlags::Error());
+ return Status::OK();
+ }
+
+ private:
+ PollableFdInfo info_;
+};
+void ServerSocketFdImplDeleter::operator()(ServerSocketFdImpl *impl) {
+ delete impl;
+}
#endif
+} // namespace detail
+
+ServerSocketFd::ServerSocketFd() = default;
+ServerSocketFd::ServerSocketFd(ServerSocketFd &&) noexcept = default;
+ServerSocketFd &ServerSocketFd::operator=(ServerSocketFd &&) noexcept = default;
+ServerSocketFd::~ServerSocketFd() = default;
+ServerSocketFd::ServerSocketFd(unique_ptr<detail::ServerSocketFdImpl> impl) : impl_(impl.release()) {
+}
+PollableFdInfo &ServerSocketFd::get_poll_info() {
+ return impl_->get_poll_info();
+}
+
+const PollableFdInfo &ServerSocketFd::get_poll_info() const {
+ return impl_->get_poll_info();
+}
+
+Status ServerSocketFd::get_pending_error() {
+ return impl_->get_pending_error();
+}
+
+const NativeFd &ServerSocketFd::get_native_fd() const {
+ return impl_->get_native_fd();
+}
+
+Result<SocketFd> ServerSocketFd::accept() {
+ return impl_->accept();
}
void ServerSocketFd::close() {
- fd_.close();
+ impl_.reset();
}
bool ServerSocketFd::empty() const {
- return fd_.empty();
+ return !impl_;
}
-Status ServerSocketFd::init(int32 port, CSlice addr) {
- IPAddress address;
- TRY_STATUS(address.init_ipv4_port(addr, port));
- auto fd = socket(address.get_address_family(), SOCK_STREAM, 0);
-#if TD_PORT_POSIX
- if (fd == -1) {
-#elif TD_PORT_WINDOWS
- if (fd == INVALID_SOCKET) {
-#endif
+Result<ServerSocketFd> ServerSocketFd::open(int32 port, CSlice addr) {
+ if (port <= 0 || port >= (1 << 16)) {
+ return Status::Error(PSLICE() << "Invalid server port " << port << " specified");
+ }
+
+ TRY_RESULT(address, IPAddress::get_ip_address(addr));
+ address.set_port(port);
+
+ NativeFd fd{socket(address.get_address_family(), SOCK_STREAM, 0)};
+ if (!fd) {
return OS_SOCKET_ERROR("Failed to create a socket");
}
- auto fd_quard = ScopeExit() + [fd]() {
-#if TD_PORT_POSIX
- ::close(fd);
-#elif TD_PORT_WINDOWS
- ::closesocket(fd);
-#endif
- };
- TRY_STATUS(detail::set_native_socket_is_blocking(fd, false));
+ TRY_STATUS(fd.set_is_blocking_unsafe(false));
+ auto sock = fd.socket();
linger ling = {0, 0};
#if TD_PORT_POSIX
int flags = 1;
#ifdef SO_REUSEPORT
- setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<const char *>(&flags), sizeof(flags));
#endif
#elif TD_PORT_WINDOWS
- BOOL flags = TRUE;
+ BOOL flags = FALSE;
+ if (address.is_ipv6()) {
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ }
+ flags = TRUE;
#endif
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
- setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
- setsockopt(fd, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char *>(&ling), sizeof(ling));
- setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(sock, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char *>(&ling), sizeof(ling));
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
- int e_bind = bind(fd, address.get_sockaddr(), static_cast<socklen_t>(address.get_sockaddr_len()));
+ int e_bind = bind(sock, address.get_sockaddr(), static_cast<socklen_t>(address.get_sockaddr_len()));
if (e_bind != 0) {
return OS_SOCKET_ERROR("Failed to bind a socket");
}
// TODO: magic constant
- int e_listen = listen(fd, 8192);
+ int e_listen = listen(sock, 8192);
if (e_listen != 0) {
return OS_SOCKET_ERROR("Failed to listen on a socket");
}
#if TD_PORT_POSIX
- fd_ = Fd(fd, Fd::Mode::Owner);
+ auto impl = make_unique<detail::ServerSocketFdImpl>(std::move(fd));
#elif TD_PORT_WINDOWS
- fd_ = Fd::create_server_socket_fd(fd, address.get_address_family());
+ auto impl = make_unique<detail::ServerSocketFdImpl>(std::move(fd), address.get_address_family());
#endif
- fd_quard.dismiss();
- return Status::OK();
+ return ServerSocketFd(std::move(impl));
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.h
index 67b43ad02d..dfff0741bd 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/ServerSocketFd.h
@@ -1,32 +1,43 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
+#include <memory>
+
namespace td {
+namespace detail {
+class ServerSocketFdImpl;
+class ServerSocketFdImplDeleter {
+ public:
+ void operator()(ServerSocketFdImpl *impl);
+};
+} // namespace detail
class ServerSocketFd {
public:
- ServerSocketFd() = default;
+ ServerSocketFd();
ServerSocketFd(const ServerSocketFd &) = delete;
ServerSocketFd &operator=(const ServerSocketFd &) = delete;
- ServerSocketFd(ServerSocketFd &&) = default;
- ServerSocketFd &operator=(ServerSocketFd &&) = default;
+ ServerSocketFd(ServerSocketFd &&) noexcept;
+ ServerSocketFd &operator=(ServerSocketFd &&) noexcept;
+ ~ServerSocketFd();
static Result<ServerSocketFd> open(int32 port, CSlice addr = CSlice("0.0.0.0")) TD_WARN_UNUSED_RESULT;
- const Fd &get_fd() const;
- Fd &get_fd();
- int32 get_flags() const;
+ PollableFdInfo &get_poll_info();
+ const PollableFdInfo &get_poll_info() const;
+
Status get_pending_error() TD_WARN_UNUSED_RESULT;
Result<SocketFd> accept() TD_WARN_UNUSED_RESULT;
@@ -34,10 +45,10 @@ class ServerSocketFd {
void close();
bool empty() const;
- private:
- Fd fd_;
+ const NativeFd &get_native_fd() const;
- Status init(int32 port, CSlice addr) TD_WARN_UNUSED_RESULT;
+ private:
+ std::unique_ptr<detail::ServerSocketFdImpl, detail::ServerSocketFdImplDeleter> impl_;
+ explicit ServerSocketFd(unique_ptr<detail::ServerSocketFdImpl> impl);
};
-
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.cpp
index 790bcd1bbd..90d516db36 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.cpp
@@ -1,18 +1,31 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/port/SocketFd.h"
+#include "td/utils/common.h"
+#include "td/utils/format.h"
#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/detail/skip_eintr.h"
+#include "td/utils/port/PollFlags.h"
+#include "td/utils/SliceBuilder.h"
#if TD_PORT_WINDOWS
-#include "td/utils/misc.h"
+#include "td/utils/buffer.h"
+#include "td/utils/port/detail/Iocp.h"
+#include "td/utils/port/Mutex.h"
+#include "td/utils/VectorQueue.h"
+
+#include <limits>
#endif
#if TD_PORT_POSIX
+#include <cerrno>
+
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
@@ -22,118 +35,670 @@
#include <unistd.h>
#endif
+#include <atomic>
+#include <cstring>
+
namespace td {
+namespace detail {
+#if TD_PORT_WINDOWS
+class SocketFdImpl final : private Iocp::Callback {
+ public:
+ explicit SocketFdImpl(NativeFd native_fd) : info_(std::move(native_fd)) {
+ VLOG(fd) << get_native_fd() << " create from native_fd";
+ get_poll_info().add_flags(PollFlags::Write());
+ Iocp::get()->subscribe(get_native_fd(), this);
+ is_read_active_ = true;
+ notify_iocp_connected();
+ }
-Result<SocketFd> SocketFd::open(const IPAddress &address) {
- SocketFd socket;
- TRY_STATUS(socket.init(address));
- return std::move(socket);
-}
+ SocketFdImpl(NativeFd native_fd, const IPAddress &addr) : info_(std::move(native_fd)) {
+ VLOG(fd) << get_native_fd() << " create from native_fd and connect";
+ get_poll_info().add_flags(PollFlags::Write());
+ Iocp::get()->subscribe(get_native_fd(), this);
+ LPFN_CONNECTEX ConnectExPtr = nullptr;
+ GUID guid = WSAID_CONNECTEX;
+ DWORD numBytes;
+ auto error =
+ ::WSAIoctl(get_native_fd().socket(), SIO_GET_EXTENSION_FUNCTION_POINTER, static_cast<void *>(&guid),
+ sizeof(guid), static_cast<void *>(&ConnectExPtr), sizeof(ConnectExPtr), &numBytes, nullptr, nullptr);
+ if (error) {
+ on_error(OS_SOCKET_ERROR("WSAIoctl failed"));
+ return;
+ }
+ std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
+ inc_refcnt();
+ is_read_active_ = true;
+ auto status = ConnectExPtr(get_native_fd().socket(), addr.get_sockaddr(), narrow_cast<int>(addr.get_sockaddr_len()),
+ nullptr, 0, nullptr, &read_overlapped_);
-#if TD_PORT_POSIX
-Result<SocketFd> SocketFd::from_native_fd(int fd) {
- auto fd_guard = ScopeExit() + [fd]() { ::close(fd); };
+ if (status == TRUE || !check_status("Failed to connect")) {
+ is_read_active_ = false;
+ dec_refcnt();
+ }
+ }
- TRY_STATUS(detail::set_native_socket_is_blocking(fd, false));
+ void close() {
+ if (!is_write_waiting_ && is_connected_) {
+ VLOG(fd) << get_native_fd() << " will close after ongoing write";
+ auto lock = lock_.lock();
+ if (!is_write_waiting_) {
+ need_close_after_write_ = true;
+ return;
+ }
+ }
+ notify_iocp_close();
+ }
- // TODO remove copypaste
- int flags = 1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
- setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
- setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
- // TODO: SO_REUSEADDR, SO_KEEPALIVE, TCP_NODELAY, SO_SNDBUF, SO_RCVBUF, TCP_QUICKACK, SO_LINGER
+ PollableFdInfo &get_poll_info() {
+ return info_;
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return info_;
+ }
+
+ const NativeFd &get_native_fd() const {
+ return info_.native_fd();
+ }
+
+ Result<size_t> write(Slice data) {
+ // LOG(ERROR) << "Write: " << format::as_hex_dump<0>(data);
+ output_writer_.append(data);
+ return write_finish(data.size());
+ }
+
+ Result<size_t> writev(Span<IoSlice> slices) {
+ size_t total_size = 0;
+ for (auto io_slice : slices) {
+ auto size = as_slice(io_slice).size();
+ CHECK(size <= std::numeric_limits<size_t>::max() - total_size);
+ total_size += size;
+ }
+
+ auto left_size = total_size;
+ for (auto io_slice : slices) {
+ auto slice = as_slice(io_slice);
+ output_writer_.append(slice, left_size);
+ left_size -= slice.size();
+ }
+
+ return write_finish(total_size);
+ }
+
+ Result<size_t> write_finish(size_t total_size) {
+ if (is_write_waiting_) {
+ auto lock = lock_.lock();
+ is_write_waiting_ = false;
+ lock.reset();
+ notify_iocp_write();
+ }
+ return total_size;
+ }
+
+ Result<size_t> read(MutableSlice slice) {
+ if (get_poll_info().get_flags_local().has_pending_error()) {
+ TRY_STATUS(get_pending_error());
+ }
+ input_reader_.sync_with_writer();
+ auto res = input_reader_.advance(td::min(slice.size(), input_reader_.size()), slice);
+ if (res == 0) {
+ get_poll_info().clear_flags(PollFlags::Read());
+ } else {
+ // LOG(ERROR) << "Read: " << format::as_hex_dump<0>(Slice(slice.substr(0, res)));
+ }
+ return res;
+ }
+
+ Status get_pending_error() {
+ Status res;
+ {
+ auto lock = lock_.lock();
+ if (!pending_errors_.empty()) {
+ res = pending_errors_.pop();
+ }
+ if (res.is_ok()) {
+ get_poll_info().clear_flags(PollFlags::Error());
+ }
+ }
+ return res;
+ }
+
+ private:
+ PollableFdInfo info_;
+ Mutex lock_;
+
+ std::atomic<int> refcnt_{1};
+ bool close_flag_{false};
+ bool need_close_after_write_{false};
- fd_guard.dismiss();
+ std::atomic<bool> is_connected_{false};
+ bool is_read_active_{false};
+ ChainBufferWriter input_writer_;
+ ChainBufferReader input_reader_ = input_writer_.extract_reader();
+ WSAOVERLAPPED read_overlapped_;
+ VectorQueue<Status> pending_errors_;
- SocketFd socket;
- socket.fd_ = Fd(fd, Fd::Mode::Owner);
- return std::move(socket);
+ bool is_write_active_{false};
+ std::atomic<bool> is_write_waiting_{false};
+ ChainBufferWriter output_writer_;
+ ChainBufferReader output_reader_ = output_writer_.extract_reader();
+ WSAOVERLAPPED write_overlapped_;
+
+ char close_overlapped_;
+
+ bool check_status(Slice message) {
+ auto last_error = WSAGetLastError();
+ if (last_error == ERROR_IO_PENDING) {
+ return true;
+ }
+ on_error(OS_SOCKET_ERROR(message));
+ return false;
+ }
+
+ void loop_read() {
+ CHECK(is_connected_);
+ CHECK(!is_read_active_);
+ if (close_flag_ || need_close_after_write_) {
+ return;
+ }
+ std::memset(&read_overlapped_, 0, sizeof(read_overlapped_));
+ auto dest = input_writer_.prepare_append();
+ WSABUF buf;
+ buf.len = narrow_cast<ULONG>(dest.size());
+ buf.buf = dest.data();
+ DWORD flags = 0;
+ int status = WSARecv(get_native_fd().socket(), &buf, 1, nullptr, &flags, &read_overlapped_, nullptr);
+ if (status == 0 || check_status("Failed to read from connection")) {
+ inc_refcnt();
+ is_read_active_ = true;
+ }
+ }
+
+ void loop_write() {
+ CHECK(is_connected_);
+ CHECK(!is_write_active_);
+
+ output_reader_.sync_with_writer();
+ auto to_write = output_reader_.prepare_read();
+ if (to_write.empty()) {
+ auto lock = lock_.lock();
+ output_reader_.sync_with_writer();
+ to_write = output_reader_.prepare_read();
+ if (to_write.empty()) {
+ is_write_waiting_ = true;
+ if (need_close_after_write_) {
+ notify_iocp_close();
+ }
+ return;
+ }
+ }
+ if (to_write.empty()) {
+ return;
+ }
+ std::memset(&write_overlapped_, 0, sizeof(write_overlapped_));
+ constexpr size_t BUF_SIZE = 20;
+ WSABUF buf[BUF_SIZE];
+ auto it = output_reader_.clone();
+ size_t buf_i;
+ for (buf_i = 0; buf_i < BUF_SIZE; buf_i++) {
+ auto src = it.prepare_read();
+ if (src.empty()) {
+ break;
+ }
+ buf[buf_i].len = narrow_cast<ULONG>(src.size());
+ buf[buf_i].buf = const_cast<CHAR *>(src.data());
+ it.confirm_read(src.size());
+ }
+ int status =
+ WSASend(get_native_fd().socket(), buf, narrow_cast<DWORD>(buf_i), nullptr, 0, &write_overlapped_, nullptr);
+ if (status == 0 || check_status("Failed to write to connection")) {
+ inc_refcnt();
+ is_write_active_ = true;
+ }
+ }
+
+ void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) final {
+ // called from other thread
+ if (dec_refcnt() || close_flag_) {
+ VLOG(fd) << "Ignore IOCP (socket is closing)";
+ return;
+ }
+ if (r_size.is_error()) {
+ return on_error(get_socket_pending_error(get_native_fd(), overlapped, r_size.move_as_error()));
+ }
+
+ if (!is_connected_ && overlapped == &read_overlapped_) {
+ return on_connected();
+ }
+
+ auto size = r_size.move_as_ok();
+ if (overlapped == &write_overlapped_) {
+ return on_write(size);
+ }
+ if (overlapped == nullptr) {
+ CHECK(size == 0);
+ return on_write(size);
+ }
+
+ if (overlapped == &read_overlapped_) {
+ return on_read(size);
+ }
+ if (overlapped == reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_)) {
+ return on_close();
+ }
+ LOG(ERROR) << this << ' ' << overlapped << ' ' << &read_overlapped_ << ' ' << &write_overlapped_ << ' '
+ << reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_) << ' ' << size;
+ LOG(FATAL) << get_native_fd() << ' ' << info_.get_flags_local() << ' ' << refcnt_.load() << ' ' << close_flag_
+ << ' ' << need_close_after_write_ << ' ' << is_connected_ << ' ' << is_read_active_ << ' '
+ << is_write_active_ << ' ' << is_write_waiting_.load() << ' ' << input_reader_.size() << ' '
+ << output_reader_.size();
+ }
+
+ void on_error(Status status) {
+ VLOG(fd) << get_native_fd() << " on error " << status;
+ {
+ auto lock = lock_.lock();
+ pending_errors_.push(std::move(status));
+ }
+ get_poll_info().add_flags_from_poll(PollFlags::Error());
+ }
+
+ void on_connected() {
+ VLOG(fd) << get_native_fd() << " on connected";
+ CHECK(!is_connected_);
+ CHECK(is_read_active_);
+ is_connected_ = true;
+ is_read_active_ = false;
+ loop_read();
+ loop_write();
+ }
+
+ void on_read(size_t size) {
+ VLOG(fd) << get_native_fd() << " on read " << size;
+ CHECK(is_read_active_);
+ is_read_active_ = false;
+ if (size == 0) {
+ get_poll_info().add_flags_from_poll(PollFlags::Close());
+ return;
+ }
+ input_writer_.confirm_append(size);
+ get_poll_info().add_flags_from_poll(PollFlags::Read());
+ loop_read();
+ }
+
+ void on_write(size_t size) {
+ VLOG(fd) << get_native_fd() << " on write " << size;
+ if (size == 0) {
+ if (is_write_active_) {
+ return;
+ }
+ is_write_active_ = true;
+ }
+ CHECK(is_write_active_);
+ is_write_active_ = false;
+ output_reader_.advance(size);
+ loop_write();
+ }
+
+ void on_close() {
+ VLOG(fd) << get_native_fd() << " on close";
+ close_flag_ = true;
+ info_.set_native_fd({});
+ }
+ bool dec_refcnt() {
+ VLOG(fd) << get_native_fd() << " dec_refcnt from " << refcnt_;
+ if (--refcnt_ == 0) {
+ delete this;
+ return true;
+ }
+ return false;
+ }
+ void inc_refcnt() {
+ CHECK(refcnt_ != 0);
+ refcnt_++;
+ VLOG(fd) << get_native_fd() << " inc_refcnt to " << refcnt_;
+ }
+
+ void notify_iocp_write() {
+ inc_refcnt();
+ Iocp::get()->post(0, this, nullptr);
+ }
+ void notify_iocp_close() {
+ Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_));
+ }
+ void notify_iocp_connected() {
+ inc_refcnt();
+ Iocp::get()->post(0, this, &read_overlapped_);
+ }
+};
+
+void SocketFdImplDeleter::operator()(SocketFdImpl *impl) {
+ impl->close();
}
+
+class InitWSA {
+ public:
+ InitWSA() {
+ /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSADATA wsaData;
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) {
+ auto error = OS_SOCKET_ERROR("Failed to init WSA");
+ LOG(FATAL) << error;
+ }
+ }
+};
+
+static InitWSA init_wsa;
+
+#else
+class SocketFdImpl {
+ public:
+ PollableFdInfo info_;
+ explicit SocketFdImpl(NativeFd fd) : info_(std::move(fd)) {
+ }
+ PollableFdInfo &get_poll_info() {
+ return info_;
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return info_;
+ }
+
+ const NativeFd &get_native_fd() const {
+ return info_.native_fd();
+ }
+
+ Result<size_t> writev(Span<IoSlice> slices) {
+ int native_fd = get_native_fd().socket();
+ TRY_RESULT(slices_size, narrow_cast_safe<int>(slices.size()));
+ auto write_res = detail::skip_eintr([&] {
+ // sendmsg can erroneously return 2^32 - 1 on Android 5.1 and Android 6.0, so it must not be used there
+#if defined(MSG_NOSIGNAL) && !TD_ANDROID
+ msghdr msg;
+ std::memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = const_cast<iovec *>(slices.begin());
+ msg.msg_iovlen = slices_size;
+ return sendmsg(native_fd, &msg, MSG_NOSIGNAL);
+#else
+ return ::writev(native_fd, slices.begin(), slices_size);
+#endif
+ });
+ if (write_res >= 0) {
+ auto result = narrow_cast<size_t>(write_res);
+ auto left = result;
+ for (const auto &slice : slices) {
+ if (left <= slice.iov_len) {
+ return result;
+ }
+ left -= slice.iov_len;
+ }
+ LOG(FATAL) << "Receive " << write_res << " as writev response, but tried to write only " << result - left
+ << " bytes";
+ }
+ return write_finish();
+ }
+
+ Result<size_t> write(Slice slice) {
+ int native_fd = get_native_fd().socket();
+ auto write_res = detail::skip_eintr([&] {
+ return
+#ifdef MSG_NOSIGNAL
+ send(native_fd, slice.begin(), slice.size(), MSG_NOSIGNAL);
+#else
+ ::write(native_fd, slice.begin(), slice.size());
#endif
+ });
+ if (write_res >= 0) {
+ auto result = narrow_cast<size_t>(write_res);
+ LOG_CHECK(result <= slice.size()) << "Receive " << write_res << " as write response, but tried to write only "
+ << slice.size() << " bytes";
+ return result;
+ }
+ return write_finish();
+ }
-Status SocketFd::init(const IPAddress &address) {
- auto fd = socket(address.get_address_family(), SOCK_STREAM, 0);
-#if TD_PORT_POSIX
- if (fd == -1) {
-#elif TD_PORT_WINDOWS
- if (fd == INVALID_SOCKET) {
+ Result<size_t> write_finish() {
+ auto write_errno = errno;
+ if (write_errno == EAGAIN
+#if EAGAIN != EWOULDBLOCK
+ || write_errno == EWOULDBLOCK
#endif
- return OS_SOCKET_ERROR("Failed to create a socket");
+ ) {
+ get_poll_info().clear_flags(PollFlags::Write());
+ return 0;
+ }
+
+ auto error = Status::PosixError(write_errno, PSLICE() << "Write to " << get_native_fd() << " has failed");
+ switch (write_errno) {
+ case EBADF:
+ case ENXIO:
+ case EFAULT:
+ case EINVAL:
+ LOG(FATAL) << error;
+ UNREACHABLE();
+ default:
+ LOG(WARNING) << error;
+ // fallthrough
+ case ECONNRESET:
+ case EDQUOT:
+ case EFBIG:
+ case EIO:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ENOSPC:
+ case EPIPE:
+ get_poll_info().clear_flags(PollFlags::Write());
+ get_poll_info().add_flags(PollFlags::Close());
+ return std::move(error);
+ }
+ }
+ Result<size_t> read(MutableSlice slice) {
+ if (get_poll_info().get_flags_local().has_pending_error()) {
+ TRY_STATUS(get_pending_error());
+ }
+ int native_fd = get_native_fd().socket();
+ CHECK(!slice.empty());
+ auto read_res = detail::skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
+ auto read_errno = errno;
+ if (read_res >= 0) {
+ if (read_res == 0) {
+ errno = 0;
+ get_poll_info().clear_flags(PollFlags::Read());
+ get_poll_info().add_flags(PollFlags::Close());
+ }
+ auto result = narrow_cast<size_t>(read_res);
+ CHECK(result <= slice.size());
+ return result;
+ }
+ if (read_errno == EAGAIN
+#if EAGAIN != EWOULDBLOCK
+ || read_errno == EWOULDBLOCK
+#endif
+ ) {
+ get_poll_info().clear_flags(PollFlags::Read());
+ return 0;
+ }
+ auto error = Status::PosixError(read_errno, PSLICE() << "Read from " << get_native_fd() << " has failed");
+ switch (read_errno) {
+ case EISDIR:
+ case EBADF:
+ case ENXIO:
+ case EFAULT:
+ case EINVAL:
+ LOG(FATAL) << error;
+ UNREACHABLE();
+ default:
+ LOG(WARNING) << error;
+ // fallthrough
+ case ENOTCONN:
+ case EIO:
+ case ENOBUFS:
+ case ENOMEM:
+ case ECONNRESET:
+ case ETIMEDOUT:
+ get_poll_info().clear_flags(PollFlags::Read());
+ get_poll_info().add_flags(PollFlags::Close());
+ return std::move(error);
+ }
}
- auto fd_quard = ScopeExit() + [fd]() {
+ Status get_pending_error() {
+ if (!get_poll_info().get_flags_local().has_pending_error()) {
+ return Status::OK();
+ }
+ TRY_STATUS(detail::get_socket_pending_error(get_native_fd()));
+ get_poll_info().clear_flags(PollFlags::Error());
+ return Status::OK();
+ }
+};
+
+void SocketFdImplDeleter::operator()(SocketFdImpl *impl) {
+ delete impl;
+}
+
+#endif
+
#if TD_PORT_POSIX
- ::close(fd);
+Status get_socket_pending_error(const NativeFd &fd) {
+ int error = 0;
+ socklen_t errlen = sizeof(error);
+ if (getsockopt(fd.socket(), SOL_SOCKET, SO_ERROR, static_cast<void *>(&error), &errlen) == 0) {
+ if (error == 0) {
+ return Status::OK();
+ }
+ return Status::PosixError(error, PSLICE() << "Error on " << fd);
+ }
+ auto status = OS_SOCKET_ERROR(PSLICE() << "Can't load error on socket " << fd);
+ LOG(INFO) << "Can't load pending socket error: " << status;
+ return status;
+}
#elif TD_PORT_WINDOWS
- ::closesocket(fd);
+Status get_socket_pending_error(const NativeFd &fd, WSAOVERLAPPED *overlapped, Status iocp_error) {
+ // We need to call WSAGetOverlappedResult() just so WSAGetLastError() will return the correct error. See
+ // https://stackoverflow.com/questions/28925003/calling-wsagetlasterror-from-an-iocp-thread-return-incorrect-result
+ DWORD num_bytes = 0;
+ DWORD flags = 0;
+ BOOL success = WSAGetOverlappedResult(fd.socket(), overlapped, &num_bytes, false, &flags);
+ if (success) {
+ LOG(ERROR) << "WSAGetOverlappedResult succeded after " << iocp_error;
+ return iocp_error;
+ }
+ return OS_SOCKET_ERROR(PSLICE() << "Error on " << fd);
+}
#endif
- };
- TRY_STATUS(detail::set_native_socket_is_blocking(fd, false));
+Status init_socket_options(NativeFd &native_fd) {
+ TRY_STATUS(native_fd.set_is_blocking_unsafe(false));
+ auto sock = native_fd.socket();
#if TD_PORT_POSIX
int flags = 1;
#elif TD_PORT_WINDOWS
BOOL flags = TRUE;
#endif
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
- setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
- setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
+#if TD_PORT_POSIX
+#ifndef MSG_NOSIGNAL // Darwin
+
+#ifdef SO_NOSIGPIPE
+ setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<const char *>(&flags), sizeof(flags));
+#else
+#warning "Failed to suppress SIGPIPE signals. Use signal(SIGPIPE, SIG_IGN) to suppress them."
+#endif
+
+#endif
+#endif
// TODO: SO_REUSEADDR, SO_KEEPALIVE, TCP_NODELAY, SO_SNDBUF, SO_RCVBUF, TCP_QUICKACK, SO_LINGER
+ return Status::OK();
+}
+
+} // namespace detail
+
+SocketFd::SocketFd() = default;
+SocketFd::SocketFd(SocketFd &&) noexcept = default;
+SocketFd &SocketFd::operator=(SocketFd &&) noexcept = default;
+SocketFd::~SocketFd() = default;
+
+SocketFd::SocketFd(unique_ptr<detail::SocketFdImpl> impl) : impl_(impl.release()) {
+}
+
+Result<SocketFd> SocketFd::from_native_fd(NativeFd fd) {
+ TRY_STATUS(detail::init_socket_options(fd));
+ return SocketFd(make_unique<detail::SocketFdImpl>(std::move(fd)));
+}
+
+Result<SocketFd> SocketFd::open(const IPAddress &address) {
+#if TD_DARWIN_WATCH_OS
+ return SocketFd{};
+#endif
+
+ NativeFd native_fd{socket(address.get_address_family(), SOCK_STREAM, IPPROTO_TCP)};
+ if (!native_fd) {
+ return OS_SOCKET_ERROR("Failed to create a socket");
+ }
+ TRY_STATUS(detail::init_socket_options(native_fd));
+
#if TD_PORT_POSIX
- int e_connect = connect(fd, address.get_sockaddr(), static_cast<socklen_t>(address.get_sockaddr_len()));
+ int e_connect =
+ connect(native_fd.socket(), address.get_sockaddr(), narrow_cast<socklen_t>(address.get_sockaddr_len()));
if (e_connect == -1) {
auto connect_errno = errno;
if (connect_errno != EINPROGRESS) {
return Status::PosixError(connect_errno, PSLICE() << "Failed to connect to " << address);
}
}
- fd_ = Fd(fd, Fd::Mode::Owner);
+ return SocketFd(make_unique<detail::SocketFdImpl>(std::move(native_fd)));
#elif TD_PORT_WINDOWS
auto bind_addr = address.get_any_addr();
- auto e_bind = bind(fd, bind_addr.get_sockaddr(), narrow_cast<int>(bind_addr.get_sockaddr_len()));
+ auto e_bind = bind(native_fd.socket(), bind_addr.get_sockaddr(), narrow_cast<int>(bind_addr.get_sockaddr_len()));
if (e_bind != 0) {
return OS_SOCKET_ERROR("Failed to bind a socket");
}
-
- fd_ = Fd::create_socket_fd(fd);
- fd_.connect(address);
+ return SocketFd(make_unique<detail::SocketFdImpl>(std::move(native_fd), address));
#endif
-
- fd_quard.dismiss();
- return Status::OK();
}
-const Fd &SocketFd::get_fd() const {
- return fd_;
+void SocketFd::close() {
+ impl_.reset();
}
-Fd &SocketFd::get_fd() {
- return fd_;
+bool SocketFd::empty() const {
+ return !impl_;
}
-void SocketFd::close() {
- fd_.close();
+PollableFdInfo &SocketFd::get_poll_info() {
+ CHECK(!empty());
+ return impl_->get_poll_info();
}
-
-bool SocketFd::empty() const {
- return fd_.empty();
+const PollableFdInfo &SocketFd::get_poll_info() const {
+ CHECK(!empty());
+ return impl_->get_poll_info();
}
-int32 SocketFd::get_flags() const {
- return fd_.get_flags();
+const NativeFd &SocketFd::get_native_fd() const {
+ CHECK(!empty());
+ return impl_->get_native_fd();
}
Status SocketFd::get_pending_error() {
- return fd_.get_pending_error();
+ CHECK(!empty());
+ return impl_->get_pending_error();
}
Result<size_t> SocketFd::write(Slice slice) {
- return fd_.write(slice);
+ CHECK(!empty());
+ return impl_->write(slice);
+}
+
+Result<size_t> SocketFd::writev(Span<IoSlice> slices) {
+ CHECK(!empty());
+ return impl_->writev(slices);
}
Result<size_t> SocketFd::read(MutableSlice slice) {
- return fd_.read(slice);
+ CHECK(!empty());
+ return impl_->read(slice);
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.h
index c88dd7d789..72aa80de24 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/SocketFd.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,50 +8,63 @@
#include "td/utils/port/config.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/IoSlice.h"
#include "td/utils/port/IPAddress.h"
#include "td/utils/Slice.h"
+#include "td/utils/Span.h"
#include "td/utils/Status.h"
+#include <memory>
+
namespace td {
+namespace detail {
+class SocketFdImpl;
+class SocketFdImplDeleter {
+ public:
+ void operator()(SocketFdImpl *impl);
+};
+} // namespace detail
+
class SocketFd {
public:
- SocketFd() = default;
+ SocketFd();
SocketFd(const SocketFd &) = delete;
SocketFd &operator=(const SocketFd &) = delete;
- SocketFd(SocketFd &&) = default;
- SocketFd &operator=(SocketFd &&) = default;
+ SocketFd(SocketFd &&) noexcept;
+ SocketFd &operator=(SocketFd &&) noexcept;
+ ~SocketFd();
static Result<SocketFd> open(const IPAddress &address) TD_WARN_UNUSED_RESULT;
- const Fd &get_fd() const;
- Fd &get_fd();
-
- int32 get_flags() const;
+ PollableFdInfo &get_poll_info();
+ const PollableFdInfo &get_poll_info() const;
Status get_pending_error() TD_WARN_UNUSED_RESULT;
Result<size_t> write(Slice slice) TD_WARN_UNUSED_RESULT;
+ Result<size_t> writev(Span<IoSlice> slices) TD_WARN_UNUSED_RESULT;
Result<size_t> read(MutableSlice slice) TD_WARN_UNUSED_RESULT;
+ const NativeFd &get_native_fd() const;
+ static Result<SocketFd> from_native_fd(NativeFd fd);
+
void close();
bool empty() const;
private:
- Fd fd_;
-
- friend class ServerSocketFd;
-
- Status init(const IPAddress &address) TD_WARN_UNUSED_RESULT;
+ std::unique_ptr<detail::SocketFdImpl, detail::SocketFdImplDeleter> impl_;
+ explicit SocketFd(unique_ptr<detail::SocketFdImpl> impl);
+};
+namespace detail {
#if TD_PORT_POSIX
- static Result<SocketFd> from_native_fd(int fd);
+Status get_socket_pending_error(const NativeFd &fd);
+#elif TD_PORT_WINDOWS
+Status get_socket_pending_error(const NativeFd &fd, WSAOVERLAPPED *overlapped, Status iocp_error);
#endif
-#if TD_PORT_WINDOWS
- explicit SocketFd(Fd fd) : fd_(std::move(fd)) {
- }
-#endif
-};
+} // namespace detail
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.cpp
index edc882761b..3000598719 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,7 +14,9 @@
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/Clocks.h"
+#include "td/utils/port/detail/skip_eintr.h"
#include "td/utils/ScopeGuard.h"
+#include "td/utils/SliceBuilder.h"
#include <utility>
@@ -37,8 +39,24 @@
#include <sys/syscall.h>
#endif
+#elif TD_PORT_WINDOWS
+
+#include "td/utils/port/thread.h"
+
+#ifndef PSAPI_VERSION
+#define PSAPI_VERSION 1
+#endif
+#ifdef __MINGW32__
+#include <psapi.h>
+#else
+#include <Psapi.h>
+#endif
+
+#endif
+
namespace td {
+#if TD_PORT_POSIX
namespace detail {
template <class...>
@@ -56,6 +74,19 @@ struct TimeNsec {
}
};
+// remove libc compatibility hacks if any: we have our own hacks
+#ifdef st_atimespec
+#undef st_atimespec
+#endif
+
+#ifdef st_atimensec
+#undef st_atimensec
+#endif
+
+#ifdef st_atime_nsec
+#undef st_atime_nsec
+#endif
+
template <class T>
struct TimeNsec<T, void_t<char, decltype(T::st_atimespec), decltype(T::st_mtimespec)>> {
static std::pair<decltype(decltype(T::st_atimespec)::tv_nsec), decltype(decltype(T::st_mtimespec)::tv_nsec)> get(
@@ -78,6 +109,13 @@ struct TimeNsec<T, void_t<int, decltype(T::st_atim), decltype(T::st_mtim)>> {
}
};
+template <class T>
+struct TimeNsec<T, void_t<long, decltype(T::st_atime_nsec), decltype(T::st_mtime_nsec)>> {
+ static std::pair<decltype(T::st_atime_nsec), decltype(T::st_mtime_nsec)> get(const T &s) {
+ return {s.st_atime_nsec, s.st_mtime_nsec};
+ }
+};
+
Stat from_native_stat(const struct ::stat &buf) {
auto time_nsec = TimeNsec<struct ::stat>::get(buf);
@@ -85,16 +123,17 @@ Stat from_native_stat(const struct ::stat &buf) {
res.atime_nsec_ = static_cast<uint64>(buf.st_atime) * 1000000000 + time_nsec.first;
res.mtime_nsec_ = static_cast<uint64>(buf.st_mtime) * 1000000000 + time_nsec.second / 1000 * 1000;
res.size_ = buf.st_size;
+ res.real_size_ = buf.st_blocks * 512;
res.is_dir_ = (buf.st_mode & S_IFMT) == S_IFDIR;
res.is_reg_ = (buf.st_mode & S_IFMT) == S_IFREG;
return res;
}
-Stat fstat(int native_fd) {
+Result<Stat> fstat(int native_fd) {
struct ::stat buf;
- int err = fstat(native_fd, &buf);
- auto fstat_errno = errno;
- LOG_IF(FATAL, err < 0) << Status::PosixError(fstat_errno, PSLICE() << "stat for fd " << native_fd << " failed");
+ if (detail::skip_eintr([&] { return ::fstat(native_fd, &buf); }) < 0) {
+ return OS_ERROR(PSLICE() << "Stat for fd " << native_fd << " failed");
+ }
return detail::from_native_stat(buf);
}
@@ -103,8 +142,10 @@ Status update_atime(int native_fd) {
timespec times[2];
// access time
times[0].tv_nsec = UTIME_NOW;
+ times[0].tv_sec = 0;
// modify time
times[1].tv_nsec = UTIME_OMIT;
+ times[1].tv_sec = 0;
if (futimens(native_fd, times) < 0) {
auto status = OS_ERROR(PSLICE() << "futimens " << tag("fd", native_fd));
LOG(WARNING) << status;
@@ -112,7 +153,7 @@ Status update_atime(int native_fd) {
}
return Status::OK();
#elif TD_DARWIN
- auto info = fstat(native_fd);
+ TRY_RESULT(info, fstat(native_fd));
timeval upd[2];
auto now = Clocks::system();
// access time
@@ -150,15 +191,22 @@ Status update_atime(CSlice path) {
SCOPE_EXIT {
file.close();
};
- return detail::update_atime(file.get_native_fd());
+ return detail::update_atime(file.get_native_fd().fd());
}
+#endif
Result<Stat> stat(CSlice path) {
+#if TD_PORT_POSIX
struct ::stat buf;
- if (stat(path.c_str(), &buf) < 0) {
- return OS_ERROR(PSLICE() << "stat for " << tag("file", path) << " failed");
+ int err = detail::skip_eintr([&] { return ::stat(path.c_str(), &buf); });
+ if (err < 0) {
+ return OS_ERROR(PSLICE() << "Stat for file \"" << path << "\" failed");
}
return detail::from_native_stat(buf);
+#elif TD_PORT_WINDOWS
+ TRY_RESULT(fd, FileFd::open(path, FileFd::Flags::Read | FileFd::PrivateFlags::WinStat));
+ return fd.stat();
+#endif
}
Result<MemStat> mem_stat() {
@@ -168,7 +216,7 @@ Result<MemStat> mem_stat() {
if (KERN_SUCCESS !=
task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&t_info), &t_info_count)) {
- return Status::Error("task_info failed");
+ return Status::Error("Call to task_info failed");
}
MemStat res;
res.resident_size_ = t_info.resident_size;
@@ -226,7 +274,7 @@ Result<MemStat> mem_stat() {
LOG(ERROR) << "Failed to parse memory stats " << tag("name", name) << tag("value", value);
*x = static_cast<uint64>(-1);
} else {
- *x = r_mem.ok() * 1024; // memory is in kB
+ *x = r_mem.ok() * 1024; // memory is in KB
}
}
if (*s == 0) {
@@ -236,6 +284,21 @@ Result<MemStat> mem_stat() {
}
return res;
+#elif TD_WINDOWS
+ PROCESS_MEMORY_COUNTERS_EX counters;
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS *>(&counters),
+ sizeof(counters))) {
+ return Status::Error("Call to GetProcessMemoryInfo failed");
+ }
+
+ // Working set = all non-virtual memory in RAM, including memory-mapped files
+ // PrivateUsage = Commit charge = all non-virtual memory in RAM and swap file, but not in memory-mapped files
+ MemStat res;
+ res.resident_size_ = counters.WorkingSetSize;
+ res.resident_size_peak_ = counters.PeakWorkingSetSize;
+ res.virtual_size_ = counters.PrivateUsage;
+ res.virtual_size_peak_ = counters.PeakPagefileUsage;
+ return res;
#else
return Status::Error("Not supported");
#endif
@@ -251,7 +314,9 @@ Status cpu_stat_self(CpuStat &stat) {
constexpr int TMEM_SIZE = 10000;
char mem[TMEM_SIZE];
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
- CHECK(size < TMEM_SIZE - 1);
+ if (size >= TMEM_SIZE - 1) {
+ return Status::Error("Failed for read /proc/self/stat");
+ }
mem[size] = 0;
char *s = mem;
@@ -260,10 +325,10 @@ Status cpu_stat_self(CpuStat &stat) {
while (pass_cnt < 15) {
if (pass_cnt == 13) {
- stat.process_user_ticks = to_integer<uint64>(Slice(s, t));
+ stat.process_user_ticks_ = to_integer<uint64>(Slice(s, t));
}
if (pass_cnt == 14) {
- stat.process_system_ticks = to_integer<uint64>(Slice(s, t));
+ stat.process_system_ticks_ = to_integer<uint64>(Slice(s, t));
}
while (*s && *s != ' ') {
s++;
@@ -272,11 +337,12 @@ Status cpu_stat_self(CpuStat &stat) {
s++;
pass_cnt++;
} else {
- return Status::Error("unexpected end of proc file");
+ return Status::Error("Unexpected end of proc file");
}
}
return Status::OK();
}
+
Status cpu_stat_total(CpuStat &stat) {
TRY_RESULT(fd, FileFd::open("/proc/stat", FileFd::Read));
SCOPE_EXIT {
@@ -286,14 +352,17 @@ Status cpu_stat_total(CpuStat &stat) {
constexpr int TMEM_SIZE = 10000;
char mem[TMEM_SIZE];
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
- CHECK(size < TMEM_SIZE - 1);
+ if (size >= TMEM_SIZE - 1) {
+ return Status::Error("Failed for read /proc/stat");
+ }
mem[size] = 0;
- uint64 sum = 0, cur = 0;
+ uint64 sum = 0;
+ uint64 cur = 0;
for (size_t i = 0; i < size; i++) {
- int c = mem[i];
+ char c = mem[i];
if (c >= '0' && c <= '9') {
- cur = cur * 10 + (uint64)c - '0';
+ cur = cur * 10 + static_cast<uint64>(c) - '0';
} else {
sum += cur;
cur = 0;
@@ -303,7 +372,7 @@ Status cpu_stat_total(CpuStat &stat) {
}
}
- stat.total_ticks = sum;
+ stat.total_ticks_ = sum;
return Status::OK();
}
#endif
@@ -314,24 +383,27 @@ Result<CpuStat> cpu_stat() {
TRY_STATUS(cpu_stat_self(stat));
TRY_STATUS(cpu_stat_total(stat));
return stat;
-#else
- return Status::Error("Not supported");
-#endif
-}
-} // namespace td
-#endif
-
-#if TD_PORT_WINDOWS
-namespace td {
+#elif TD_WINDOWS
+ CpuStat stat;
+ stat.total_ticks_ = static_cast<uint64>(GetTickCount64()) * 10000;
+ auto hardware_concurrency = thread::hardware_concurrency();
+ if (hardware_concurrency != 0) {
+ stat.total_ticks_ *= hardware_concurrency;
+ }
-Result<Stat> stat(CSlice path) {
- TRY_RESULT(fd, FileFd::open(path, FileFd::Flags::Read));
- return fd.stat();
-}
+ FILETIME ignored_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+ if (!GetProcessTimes(GetCurrentProcess(), &ignored_time, &ignored_time, &kernel_time, &user_time)) {
+ return Status::Error("Failed to call GetProcessTimes");
+ }
+ stat.process_system_ticks_ = kernel_time.dwLowDateTime + (static_cast<uint64>(kernel_time.dwHighDateTime) << 32);
+ stat.process_user_ticks_ = user_time.dwLowDateTime + (static_cast<uint64>(user_time.dwHighDateTime) << 32);
-Result<CpuStat> cpu_stat() {
+ return stat;
+#else
return Status::Error("Not supported");
+#endif
}
} // namespace td
-#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.h
index d0a98db141..ed8fc3de82 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/Stat.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -18,6 +18,7 @@ struct Stat {
bool is_dir_;
bool is_reg_;
int64 size_;
+ int64 real_size_;
uint64 atime_nsec_;
uint64 mtime_nsec_;
};
@@ -25,19 +26,12 @@ struct Stat {
Result<Stat> stat(CSlice path) TD_WARN_UNUSED_RESULT;
struct CpuStat {
- uint64 total_ticks{0};
- uint64 process_user_ticks{0};
- uint64 process_system_ticks{0};
+ uint64 total_ticks_{0};
+ uint64 process_user_ticks_{0};
+ uint64 process_system_ticks_{0};
};
-Result<CpuStat> cpu_stat() TD_WARN_UNUSED_RESULT;
-
-#if TD_PORT_POSIX
-
-namespace detail {
-Stat fstat(int native_fd); // TODO return Result<Stat>
-} // namespace detail
-Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT;
+Result<CpuStat> cpu_stat() TD_WARN_UNUSED_RESULT;
struct MemStat {
uint64 resident_size_ = 0;
@@ -48,6 +42,14 @@ struct MemStat {
Result<MemStat> mem_stat() TD_WARN_UNUSED_RESULT;
+#if TD_PORT_POSIX
+
+namespace detail {
+Result<Stat> fstat(int native_fd);
+} // namespace detail
+
+Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT;
+
#endif
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.cpp
new file mode 100644
index 0000000000..b2064a9fe6
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.cpp
@@ -0,0 +1,251 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/StdStreams.h"
+
+#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/detail/Iocp.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/PollFlags.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/ScopeGuard.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+
+#include <atomic>
+
+namespace td {
+
+#if TD_PORT_POSIX
+template <int id>
+static FileFd &get_file_fd() {
+ static FileFd result = FileFd::from_native_fd(NativeFd(id, true));
+ static auto guard = ScopeExit() + [&] {
+ result.move_as_native_fd().release();
+ };
+ return result;
+}
+
+FileFd &Stdin() {
+ return get_file_fd<0>();
+}
+FileFd &Stdout() {
+ return get_file_fd<1>();
+}
+FileFd &Stderr() {
+ return get_file_fd<2>();
+}
+#elif TD_PORT_WINDOWS
+template <DWORD id>
+static FileFd &get_file_fd() {
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+ static auto handle = GetStdHandle(id);
+ LOG_IF(FATAL, handle == INVALID_HANDLE_VALUE) << "Failed to GetStdHandle " << id;
+ static FileFd result = handle == nullptr ? FileFd() : FileFd::from_native_fd(NativeFd(handle, true));
+ static auto guard = ScopeExit() + [&] {
+ if (handle != nullptr) {
+ result.move_as_native_fd().release();
+ }
+ };
+#else
+ static FileFd result;
+#endif
+ return result;
+}
+
+FileFd &Stdin() {
+ return get_file_fd<STD_INPUT_HANDLE>();
+}
+FileFd &Stdout() {
+ return get_file_fd<STD_OUTPUT_HANDLE>();
+}
+FileFd &Stderr() {
+ return get_file_fd<STD_ERROR_HANDLE>();
+}
+#endif
+
+#if TD_PORT_WINDOWS
+namespace detail {
+class BufferedStdinImpl final : private Iocp::Callback {
+ public:
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+ BufferedStdinImpl() : info_(NativeFd(GetStdHandle(STD_INPUT_HANDLE), true)) {
+ iocp_ref_ = Iocp::get()->get_ref();
+ read_thread_ = thread([this] { this->read_loop(); });
+ }
+#else
+ BufferedStdinImpl() {
+ close();
+ }
+#endif
+ BufferedStdinImpl(const BufferedStdinImpl &) = delete;
+ BufferedStdinImpl &operator=(const BufferedStdinImpl &) = delete;
+ BufferedStdinImpl(BufferedStdinImpl &&) = delete;
+ BufferedStdinImpl &operator=(BufferedStdinImpl &&) = delete;
+ ~BufferedStdinImpl() {
+ info_.move_as_native_fd().release();
+ }
+ void close() {
+ close_flag_ = true;
+ }
+
+ ChainBufferReader &input_buffer() {
+ return reader_;
+ }
+
+ PollableFdInfo &get_poll_info() {
+ return info_;
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return info_;
+ }
+
+ Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT {
+ info_.sync_with_poll();
+ info_.clear_flags(PollFlags::Read());
+ reader_.sync_with_writer();
+ return reader_.size();
+ }
+
+ private:
+ PollableFdInfo info_;
+ ChainBufferWriter writer_;
+ ChainBufferReader reader_ = writer_.extract_reader();
+ thread read_thread_;
+ std::atomic<bool> close_flag_{false};
+ IocpRef iocp_ref_;
+ std::atomic<int> refcnt_{1};
+
+ void read_loop() {
+ while (!close_flag_) {
+ auto slice = writer_.prepare_append();
+ auto r_size = read(slice);
+ if (r_size.is_error()) {
+ LOG(ERROR) << "Stop read stdin loop: " << r_size.error();
+ break;
+ }
+ writer_.confirm_append(r_size.ok());
+ inc_refcnt();
+ if (!iocp_ref_.post(0, this, nullptr)) {
+ dec_refcnt();
+ }
+ }
+ if (!iocp_ref_.post(0, this, nullptr)) {
+ read_thread_.detach();
+ dec_refcnt();
+ }
+ }
+ void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) final {
+ info_.add_flags_from_poll(PollFlags::Read());
+ dec_refcnt();
+ }
+
+ bool dec_refcnt() {
+ if (--refcnt_ == 0) {
+ delete this;
+ return true;
+ }
+ return false;
+ }
+ void inc_refcnt() {
+ CHECK(refcnt_ != 0);
+ refcnt_++;
+ }
+
+ Result<size_t> read(MutableSlice slice) {
+ auto native_fd = info_.native_fd().fd();
+ DWORD bytes_read = 0;
+ auto res = ReadFile(native_fd, slice.data(), narrow_cast<DWORD>(slice.size()), &bytes_read, nullptr);
+ if (res) {
+ return static_cast<size_t>(bytes_read);
+ }
+ return OS_ERROR(PSLICE() << "Read from " << info_.native_fd() << " has failed");
+ }
+};
+void BufferedStdinImplDeleter::operator()(BufferedStdinImpl *impl) {
+ // LOG(ERROR) << "Close";
+ impl->close();
+}
+} // namespace detail
+#elif TD_PORT_POSIX
+namespace detail {
+class BufferedStdinImpl {
+ public:
+ BufferedStdinImpl() {
+ file_fd_ = FileFd::from_native_fd(NativeFd(Stdin().get_native_fd().fd()));
+ file_fd_.get_native_fd().set_is_blocking(false);
+ }
+ BufferedStdinImpl(const BufferedStdinImpl &) = delete;
+ BufferedStdinImpl &operator=(const BufferedStdinImpl &) = delete;
+ BufferedStdinImpl(BufferedStdinImpl &&) = delete;
+ BufferedStdinImpl &operator=(BufferedStdinImpl &&) = delete;
+ ~BufferedStdinImpl() {
+ file_fd_.get_native_fd().set_is_blocking(true);
+ file_fd_.move_as_native_fd().release();
+ }
+
+ ChainBufferReader &input_buffer() {
+ return reader_;
+ }
+
+ PollableFdInfo &get_poll_info() {
+ return file_fd_.get_poll_info();
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return file_fd_.get_poll_info();
+ }
+
+ Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT {
+ size_t result = 0;
+ ::td::sync_with_poll(*this);
+ while (::td::can_read_local(*this) && max_read) {
+ MutableSlice slice = writer_.prepare_append();
+ slice.truncate(max_read);
+ TRY_RESULT(x, file_fd_.read(slice));
+ slice.truncate(x);
+ writer_.confirm_append(x);
+ result += x;
+ max_read -= x;
+ }
+ if (result) {
+ reader_.sync_with_writer();
+ }
+ return result;
+ }
+
+ private:
+ FileFd file_fd_;
+ ChainBufferWriter writer_;
+ ChainBufferReader reader_ = writer_.extract_reader();
+};
+void BufferedStdinImplDeleter::operator()(BufferedStdinImpl *impl) {
+ delete impl;
+}
+} // namespace detail
+#endif
+
+BufferedStdin::BufferedStdin() : impl_(make_unique<detail::BufferedStdinImpl>().release()) {
+}
+BufferedStdin::BufferedStdin(BufferedStdin &&) noexcept = default;
+BufferedStdin &BufferedStdin::operator=(BufferedStdin &&) noexcept = default;
+BufferedStdin::~BufferedStdin() = default;
+
+ChainBufferReader &BufferedStdin::input_buffer() {
+ return impl_->input_buffer();
+}
+PollableFdInfo &BufferedStdin::get_poll_info() {
+ return impl_->get_poll_info();
+}
+const PollableFdInfo &BufferedStdin::get_poll_info() const {
+ return impl_->get_poll_info();
+}
+Result<size_t> BufferedStdin::flush_read(size_t max_read) {
+ return impl_->flush_read(max_read);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.h
new file mode 100644
index 0000000000..0922594c9a
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/StdStreams.h
@@ -0,0 +1,49 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/buffer.h"
+#include "td/utils/common.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/FileFd.h"
+#include "td/utils/Status.h"
+
+#include <limits>
+#include <memory>
+
+namespace td {
+
+FileFd &Stdin();
+FileFd &Stdout();
+FileFd &Stderr();
+
+namespace detail {
+class BufferedStdinImpl;
+class BufferedStdinImplDeleter {
+ public:
+ void operator()(BufferedStdinImpl *impl);
+};
+} // namespace detail
+
+class BufferedStdin {
+ public:
+ BufferedStdin();
+ BufferedStdin(const BufferedStdin &) = delete;
+ BufferedStdin &operator=(const BufferedStdin &) = delete;
+ BufferedStdin(BufferedStdin &&) noexcept;
+ BufferedStdin &operator=(BufferedStdin &&) noexcept;
+ ~BufferedStdin();
+ ChainBufferReader &input_buffer();
+ PollableFdInfo &get_poll_info();
+ const PollableFdInfo &get_poll_info() const;
+ Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT;
+
+ private:
+ std::unique_ptr<detail::BufferedStdinImpl, detail::BufferedStdinImplDeleter> impl_;
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.cpp
new file mode 100644
index 0000000000..eee487d44b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.cpp
@@ -0,0 +1,873 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/UdpSocketFd.h"
+
+#include "td/utils/common.h"
+#include "td/utils/format.h"
+#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/detail/skip_eintr.h"
+#include "td/utils/port/PollFlags.h"
+#include "td/utils/port/SocketFd.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/VectorQueue.h"
+
+#if TD_PORT_WINDOWS
+#include "td/utils/port/detail/Iocp.h"
+#include "td/utils/port/Mutex.h"
+#endif
+
+#if TD_PORT_POSIX
+#include <cerrno>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if TD_LINUX
+#include <linux/errqueue.h>
+#endif
+#endif
+
+#include <array>
+#include <atomic>
+#include <cstring>
+
+namespace td {
+namespace detail {
+#if TD_PORT_WINDOWS
+class UdpSocketReceiveHelper {
+ public:
+ void to_native(const UdpMessage &message, WSAMSG &message_header) {
+ socklen_t addr_len{narrow_cast<socklen_t>(sizeof(addr_))};
+ message_header.name = reinterpret_cast<sockaddr *>(&addr_);
+ message_header.namelen = addr_len;
+ buf_.buf = const_cast<char *>(message.data.as_slice().begin());
+ buf_.len = narrow_cast<DWORD>(message.data.size());
+ message_header.lpBuffers = &buf_;
+ message_header.dwBufferCount = 1;
+ message_header.Control.buf = nullptr; // control_buf_.data();
+ message_header.Control.len = 0; // narrow_cast<decltype(message_header.Control.len)>(control_buf_.size());
+ message_header.dwFlags = 0;
+ }
+
+ static void from_native(WSAMSG &message_header, size_t message_size, UdpMessage &message) {
+ message.address.init_sockaddr(reinterpret_cast<sockaddr *>(message_header.name), message_header.namelen).ignore();
+ message.error = Status::OK();
+
+ if ((message_header.dwFlags & (MSG_TRUNC | MSG_CTRUNC)) != 0) {
+ message.error = Status::Error(501, "Message too long");
+ message.data = BufferSlice();
+ return;
+ }
+
+ CHECK(message_size <= message.data.size());
+ message.data.truncate(message_size);
+ CHECK(message_size == message.data.size());
+ }
+
+ private:
+ std::array<char, 1024> control_buf_;
+ sockaddr_storage addr_;
+ WSABUF buf_;
+};
+
+class UdpSocketSendHelper {
+ public:
+ void to_native(const UdpMessage &message, WSAMSG &message_header) {
+ message_header.name = const_cast<sockaddr *>(message.address.get_sockaddr());
+ message_header.namelen = narrow_cast<socklen_t>(message.address.get_sockaddr_len());
+ buf_.buf = const_cast<char *>(message.data.as_slice().begin());
+ buf_.len = narrow_cast<DWORD>(message.data.size());
+ message_header.lpBuffers = &buf_;
+ message_header.dwBufferCount = 1;
+
+ message_header.Control.buf = nullptr;
+ message_header.Control.len = 0;
+ message_header.dwFlags = 0;
+ }
+
+ private:
+ WSABUF buf_;
+};
+
+class UdpSocketFdImpl final : private Iocp::Callback {
+ public:
+ explicit UdpSocketFdImpl(NativeFd fd) : info_(std::move(fd)) {
+ get_poll_info().add_flags(PollFlags::Write());
+ Iocp::get()->subscribe(get_native_fd(), this);
+ is_receive_active_ = true;
+ notify_iocp_connected();
+ }
+ PollableFdInfo &get_poll_info() {
+ return info_;
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return info_;
+ }
+
+ const NativeFd &get_native_fd() const {
+ return info_.native_fd();
+ }
+
+ void close() {
+ notify_iocp_close();
+ }
+
+ Result<optional<UdpMessage>> receive() {
+ auto lock = lock_.lock();
+ if (!pending_errors_.empty()) {
+ auto status = pending_errors_.pop();
+ if (!UdpSocketFd::is_critical_read_error(status)) {
+ return UdpMessage{{}, {}, std::move(status)};
+ }
+ return std::move(status);
+ }
+ if (!receive_queue_.empty()) {
+ return receive_queue_.pop();
+ }
+
+ return optional<UdpMessage>{};
+ }
+
+ void send(UdpMessage message) {
+ auto lock = lock_.lock();
+ send_queue_.push(std::move(message));
+ }
+
+ Status flush_send() {
+ if (is_send_waiting_) {
+ auto lock = lock_.lock();
+ is_send_waiting_ = false;
+ notify_iocp_send();
+ }
+ return Status::OK();
+ }
+
+ private:
+ PollableFdInfo info_;
+ Mutex lock_;
+
+ std::atomic<int> refcnt_{1};
+ bool is_connected_{false};
+ bool close_flag_{false};
+
+ bool is_send_active_{false};
+ bool is_send_waiting_{false};
+ VectorQueue<UdpMessage> send_queue_;
+ WSAOVERLAPPED send_overlapped_;
+
+ bool is_receive_active_{false};
+ VectorQueue<UdpMessage> receive_queue_;
+ VectorQueue<Status> pending_errors_;
+ UdpMessage to_receive_;
+ WSAMSG receive_message_;
+ UdpSocketReceiveHelper receive_helper_;
+ static constexpr size_t MAX_PACKET_SIZE = 2048;
+ static constexpr size_t RESERVED_SIZE = MAX_PACKET_SIZE * 8;
+ BufferSlice receive_buffer_;
+
+ UdpMessage to_send_;
+ WSAOVERLAPPED receive_overlapped_;
+
+ char close_overlapped_;
+
+ bool check_status(Slice message) {
+ auto last_error = WSAGetLastError();
+ if (last_error == ERROR_IO_PENDING) {
+ return true;
+ }
+ on_error(OS_SOCKET_ERROR(message));
+ return false;
+ }
+
+ void loop_receive() {
+ CHECK(!is_receive_active_);
+ if (close_flag_) {
+ return;
+ }
+ std::memset(&receive_overlapped_, 0, sizeof(receive_overlapped_));
+ if (receive_buffer_.size() < MAX_PACKET_SIZE) {
+ receive_buffer_ = BufferSlice(RESERVED_SIZE);
+ }
+ to_receive_.data = receive_buffer_.clone();
+ receive_helper_.to_native(to_receive_, receive_message_);
+
+ LPFN_WSARECVMSG WSARecvMsgPtr = nullptr;
+ GUID guid = WSAID_WSARECVMSG;
+ DWORD numBytes;
+ auto error = ::WSAIoctl(get_native_fd().socket(), SIO_GET_EXTENSION_FUNCTION_POINTER, static_cast<void *>(&guid),
+ sizeof(guid), static_cast<void *>(&WSARecvMsgPtr), sizeof(WSARecvMsgPtr), &numBytes,
+ nullptr, nullptr);
+ if (error) {
+ on_error(OS_SOCKET_ERROR("WSAIoctl failed"));
+ return;
+ }
+
+ auto status = WSARecvMsgPtr(get_native_fd().socket(), &receive_message_, nullptr, &receive_overlapped_, nullptr);
+ if (status == 0 || check_status("WSARecvMsg failed")) {
+ inc_refcnt();
+ is_receive_active_ = true;
+ }
+ }
+
+ void loop_send() {
+ CHECK(!is_send_active_);
+
+ {
+ auto lock = lock_.lock();
+ if (send_queue_.empty()) {
+ is_send_waiting_ = true;
+ return;
+ }
+ to_send_ = send_queue_.pop();
+ }
+ std::memset(&send_overlapped_, 0, sizeof(send_overlapped_));
+ WSAMSG message;
+ UdpSocketSendHelper send_helper;
+ send_helper.to_native(to_send_, message);
+ auto status = WSASendMsg(get_native_fd().socket(), &message, 0, nullptr, &send_overlapped_, nullptr);
+ if (status == 0 || check_status("WSASendMsg failed")) {
+ inc_refcnt();
+ is_send_active_ = true;
+ }
+ }
+
+ void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) final {
+ // called from other thread
+ if (dec_refcnt() || close_flag_) {
+ VLOG(fd) << "Ignore IOCP (UDP socket is closing)";
+ return;
+ }
+ if (r_size.is_error()) {
+ return on_error(get_socket_pending_error(get_native_fd(), overlapped, r_size.move_as_error()));
+ }
+
+ if (!is_connected_ && overlapped == &receive_overlapped_) {
+ return on_connected();
+ }
+
+ auto size = r_size.move_as_ok();
+ if (overlapped == &send_overlapped_) {
+ return on_send(size);
+ }
+ if (overlapped == nullptr) {
+ CHECK(size == 0);
+ return on_send(size);
+ }
+
+ if (overlapped == &receive_overlapped_) {
+ return on_receive(size);
+ }
+ if (overlapped == reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_)) {
+ return on_close();
+ }
+ UNREACHABLE();
+ }
+
+ void on_error(Status status) {
+ VLOG(fd) << get_native_fd() << " on error " << status;
+ {
+ auto lock = lock_.lock();
+ pending_errors_.push(std::move(status));
+ }
+ get_poll_info().add_flags_from_poll(PollFlags::Error());
+ }
+
+ void on_connected() {
+ VLOG(fd) << get_native_fd() << " on connected";
+ CHECK(!is_connected_);
+ CHECK(is_receive_active_);
+ is_connected_ = true;
+ is_receive_active_ = false;
+ loop_receive();
+ loop_send();
+ }
+
+ void on_receive(size_t size) {
+ VLOG(fd) << get_native_fd() << " on receive " << size;
+ CHECK(is_receive_active_);
+ is_receive_active_ = false;
+ UdpSocketReceiveHelper::from_native(receive_message_, size, to_receive_);
+ receive_buffer_.confirm_read((to_receive_.data.size() + 7) & ~7);
+ {
+ auto lock = lock_.lock();
+ // LOG(ERROR) << format::escaped(to_receive_.data.as_slice());
+ receive_queue_.push(std::move(to_receive_));
+ }
+ get_poll_info().add_flags_from_poll(PollFlags::Read());
+ loop_receive();
+ }
+
+ void on_send(size_t size) {
+ VLOG(fd) << get_native_fd() << " on send " << size;
+ if (size == 0) {
+ if (is_send_active_) {
+ return;
+ }
+ is_send_active_ = true;
+ }
+ CHECK(is_send_active_);
+ is_send_active_ = false;
+ loop_send();
+ }
+
+ void on_close() {
+ VLOG(fd) << get_native_fd() << " on close";
+ close_flag_ = true;
+ info_.set_native_fd({});
+ }
+
+ bool dec_refcnt() {
+ if (--refcnt_ == 0) {
+ delete this;
+ return true;
+ }
+ return false;
+ }
+
+ void inc_refcnt() {
+ CHECK(refcnt_ != 0);
+ refcnt_++;
+ }
+
+ void notify_iocp_send() {
+ inc_refcnt();
+ Iocp::get()->post(0, this, nullptr);
+ }
+ void notify_iocp_close() {
+ Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&close_overlapped_));
+ }
+ void notify_iocp_connected() {
+ inc_refcnt();
+ Iocp::get()->post(0, this, reinterpret_cast<WSAOVERLAPPED *>(&receive_overlapped_));
+ }
+};
+
+void UdpSocketFdImplDeleter::operator()(UdpSocketFdImpl *impl) {
+ impl->close();
+}
+
+#elif TD_PORT_POSIX
+//struct iovec { [> Scatter/gather array items <]
+// void *iov_base; [> Starting address <]
+// size_t iov_len; [> Number of bytes to transfer <]
+//};
+
+//struct msghdr {
+// void *msg_name; [> optional address <]
+// socklen_t msg_namelen; [> size of address <]
+// struct iovec *msg_iov; [> scatter/gather array <]
+// size_t msg_iovlen; [> # elements in msg_iov <]
+// void *msg_control; [> ancillary data, see below <]
+// size_t msg_controllen; [> ancillary data buffer len <]
+// int msg_flags; [> flags on received message <]
+//};
+
+class UdpSocketReceiveHelper {
+ public:
+ void to_native(const UdpSocketFd::InboundMessage &message, msghdr &message_header) {
+ socklen_t addr_len{narrow_cast<socklen_t>(sizeof(addr_))};
+
+ message_header.msg_name = &addr_;
+ message_header.msg_namelen = addr_len;
+ io_vec_.iov_base = message.data.begin();
+ io_vec_.iov_len = message.data.size();
+ message_header.msg_iov = &io_vec_;
+ message_header.msg_iovlen = 1;
+ message_header.msg_control = control_buf_.data();
+ message_header.msg_controllen = narrow_cast<decltype(message_header.msg_controllen)>(control_buf_.size());
+ message_header.msg_flags = 0;
+ }
+
+ static void from_native(msghdr &message_header, size_t message_size, UdpSocketFd::InboundMessage &message) {
+#if TD_LINUX
+ cmsghdr *cmsg;
+ sock_extended_err *ee = nullptr;
+ for (cmsg = CMSG_FIRSTHDR(&message_header); cmsg != nullptr; cmsg = CMSG_NXTHDR(&message_header, cmsg)) {
+ if (cmsg->cmsg_type == IP_PKTINFO && cmsg->cmsg_level == IPPROTO_IP) {
+ //auto *pi = reinterpret_cast<in_pktinfo *>(CMSG_DATA(cmsg));
+ } else if (cmsg->cmsg_type == IPV6_PKTINFO && cmsg->cmsg_level == IPPROTO_IPV6) {
+ //auto *pi = reinterpret_cast<in6_pktinfo *>(CMSG_DATA(cmsg));
+ } else if ((cmsg->cmsg_type == IP_RECVERR && cmsg->cmsg_level == IPPROTO_IP) ||
+ (cmsg->cmsg_type == IPV6_RECVERR && cmsg->cmsg_level == IPPROTO_IPV6)) {
+ ee = reinterpret_cast<sock_extended_err *>(CMSG_DATA(cmsg));
+ }
+ }
+ if (ee != nullptr) {
+ auto *addr = reinterpret_cast<sockaddr *>(SO_EE_OFFENDER(ee));
+ IPAddress address;
+ address.init_sockaddr(addr).ignore();
+ if (message.from != nullptr) {
+ *message.from = address;
+ }
+ if (message.error) {
+ *message.error = Status::PosixError(ee->ee_errno, "");
+ }
+ //message.data = MutableSlice();
+ message.data.truncate(0);
+ return;
+ }
+#endif
+ if (message.from != nullptr) {
+ message.from->init_sockaddr(reinterpret_cast<sockaddr *>(message_header.msg_name), message_header.msg_namelen)
+ .ignore();
+ }
+ if (message.error) {
+ *message.error = Status::OK();
+ }
+ if (message_header.msg_flags & MSG_TRUNC) {
+ if (message.error) {
+ *message.error = Status::Error(501, "Message too long");
+ }
+ message.data.truncate(0);
+ return;
+ }
+ CHECK(message_size <= message.data.size());
+ message.data.truncate(message_size);
+ CHECK(message_size == message.data.size());
+ }
+
+ private:
+ std::array<char, 1024> control_buf_;
+ sockaddr_storage addr_;
+ iovec io_vec_;
+};
+
+class UdpSocketSendHelper {
+ public:
+ void to_native(const UdpSocketFd::OutboundMessage &message, msghdr &message_header) {
+ CHECK(message.to != nullptr && message.to->is_valid());
+ message_header.msg_name = const_cast<sockaddr *>(message.to->get_sockaddr());
+ message_header.msg_namelen = narrow_cast<socklen_t>(message.to->get_sockaddr_len());
+ io_vec_.iov_base = const_cast<char *>(message.data.begin());
+ io_vec_.iov_len = message.data.size();
+ message_header.msg_iov = &io_vec_;
+ message_header.msg_iovlen = 1;
+ //TODO
+ message_header.msg_control = nullptr;
+ message_header.msg_controllen = 0;
+ message_header.msg_flags = 0;
+ }
+
+ private:
+ iovec io_vec_;
+};
+
+class UdpSocketFdImpl {
+ public:
+ explicit UdpSocketFdImpl(NativeFd fd) : info_(std::move(fd)) {
+ }
+ PollableFdInfo &get_poll_info() {
+ return info_;
+ }
+ const PollableFdInfo &get_poll_info() const {
+ return info_;
+ }
+
+ const NativeFd &get_native_fd() const {
+ return info_.native_fd();
+ }
+ Status get_pending_error() {
+ if (!get_poll_info().get_flags_local().has_pending_error()) {
+ return Status::OK();
+ }
+ TRY_STATUS(detail::get_socket_pending_error(get_native_fd()));
+ get_poll_info().clear_flags(PollFlags::Error());
+ return Status::OK();
+ }
+ Status receive_message(UdpSocketFd::InboundMessage &message, bool &is_received) {
+ is_received = false;
+ int flags = 0;
+ if (get_poll_info().get_flags_local().has_pending_error()) {
+#ifdef MSG_ERRQUEUE
+ flags = MSG_ERRQUEUE;
+#else
+ return get_pending_error();
+#endif
+ }
+
+ msghdr message_header;
+ detail::UdpSocketReceiveHelper helper;
+ helper.to_native(message, message_header);
+
+ auto native_fd = get_native_fd().socket();
+ auto recvmsg_res = detail::skip_eintr([&] { return recvmsg(native_fd, &message_header, flags); });
+ auto recvmsg_errno = errno;
+ if (recvmsg_res >= 0) {
+ UdpSocketReceiveHelper::from_native(message_header, recvmsg_res, message);
+ is_received = true;
+ return Status::OK();
+ }
+ return process_recvmsg_error(recvmsg_errno, is_received);
+ }
+
+ Status process_recvmsg_error(int recvmsg_errno, bool &is_received) {
+ is_received = false;
+ if (recvmsg_errno == EAGAIN
+#if EAGAIN != EWOULDBLOCK
+ || recvmsg_errno == EWOULDBLOCK
+#endif
+ ) {
+ if (get_poll_info().get_flags_local().has_pending_error()) {
+ get_poll_info().clear_flags(PollFlags::Error());
+ } else {
+ get_poll_info().clear_flags(PollFlags::Read());
+ }
+ return Status::OK();
+ }
+
+ auto error = Status::PosixError(recvmsg_errno, PSLICE() << "Receive from " << get_native_fd() << " has failed");
+ switch (recvmsg_errno) {
+ case EBADF:
+ case EFAULT:
+ case EINVAL:
+ case ENOTCONN:
+ case ECONNRESET:
+ case ETIMEDOUT:
+ LOG(FATAL) << error;
+ UNREACHABLE();
+ default:
+ LOG(WARNING) << "Unknown error: " << error;
+ // fallthrough
+ case ENOBUFS:
+ case ENOMEM:
+#ifdef MSG_ERRQUEUE
+ get_poll_info().add_flags(PollFlags::Error());
+#endif
+ return error;
+ }
+ }
+
+ Status send_message(const UdpSocketFd::OutboundMessage &message, bool &is_sent) {
+ is_sent = false;
+ msghdr message_header;
+ detail::UdpSocketSendHelper helper;
+ helper.to_native(message, message_header);
+
+ auto native_fd = get_native_fd().socket();
+ auto sendmsg_res = detail::skip_eintr([&] { return sendmsg(native_fd, &message_header, 0); });
+ auto sendmsg_errno = errno;
+ if (sendmsg_res >= 0) {
+ is_sent = true;
+ return Status::OK();
+ }
+ return process_sendmsg_error(sendmsg_errno, is_sent);
+ }
+ Status process_sendmsg_error(int sendmsg_errno, bool &is_sent) {
+ if (sendmsg_errno == EAGAIN
+#if EAGAIN != EWOULDBLOCK
+ || sendmsg_errno == EWOULDBLOCK
+#endif
+ ) {
+ get_poll_info().clear_flags(PollFlags::Write());
+ return Status::OK();
+ }
+
+ auto error = Status::PosixError(sendmsg_errno, PSLICE() << "Send from " << get_native_fd() << " has failed");
+ switch (sendmsg_errno) {
+ // Still may send some other packets, but there is no point to resend this particular message
+ case EACCES:
+ case EMSGSIZE:
+ case EPERM:
+ LOG(WARNING) << "Silently drop packet :( " << error;
+ //TODO: get errors from MSG_ERRQUEUE is possible
+ is_sent = true;
+ return error;
+
+ // Some general problems, which may be fixed in future
+ case ENOMEM:
+ case EDQUOT:
+ case EFBIG:
+ case ENETDOWN:
+ case ENETUNREACH:
+ case ENOSPC:
+ case EHOSTUNREACH:
+ case ENOBUFS:
+ default:
+#ifdef MSG_ERRQUEUE
+ get_poll_info().add_flags(PollFlags::Error());
+#endif
+ return error;
+
+ case EBADF: // impossible
+ case ENOTSOCK: // impossible
+ case EPIPE: // impossible for udp
+ case ECONNRESET: // impossible for udp
+ case EDESTADDRREQ: // we checked that address is valid
+ case ENOTCONN: // we checked that address is valid
+ case EINTR: // we already skipped all EINTR
+ case EISCONN: // impossible for udp socket
+ case EOPNOTSUPP:
+ case ENOTDIR:
+ case EFAULT:
+ case EINVAL:
+ case EAFNOSUPPORT:
+ LOG(FATAL) << error;
+ UNREACHABLE();
+ return error;
+ }
+ }
+
+ Status send_messages(Span<UdpSocketFd::OutboundMessage> messages, size_t &cnt) {
+#if TD_HAS_MMSG
+ return send_messages_fast(messages, cnt);
+#else
+ return send_messages_slow(messages, cnt);
+#endif
+ }
+
+ Status receive_messages(MutableSpan<UdpSocketFd::InboundMessage> messages, size_t &cnt) {
+#if TD_HAS_MMSG
+ return receive_messages_fast(messages, cnt);
+#else
+ return receive_messages_slow(messages, cnt);
+#endif
+ }
+
+ private:
+ PollableFdInfo info_;
+
+ Status send_messages_slow(Span<UdpSocketFd::OutboundMessage> messages, size_t &cnt) {
+ cnt = 0;
+ for (auto &message : messages) {
+ CHECK(!message.data.empty());
+ bool is_sent;
+ auto error = send_message(message, is_sent);
+ cnt += is_sent;
+ TRY_STATUS(std::move(error));
+ }
+ return Status::OK();
+ }
+
+#if TD_HAS_MMSG
+ Status send_messages_fast(Span<UdpSocketFd::OutboundMessage> messages, size_t &cnt) {
+ //struct mmsghdr {
+ // msghdr msg_hdr; [> Message header <]
+ // unsigned int msg_len; [> Number of bytes transmitted <]
+ //};
+ std::array<detail::UdpSocketSendHelper, 16> helpers;
+ std::array<mmsghdr, 16> headers;
+ size_t to_send = min(messages.size(), headers.size());
+ for (size_t i = 0; i < to_send; i++) {
+ helpers[i].to_native(messages[i], headers[i].msg_hdr);
+ headers[i].msg_len = 0;
+ }
+
+ auto native_fd = get_native_fd().socket();
+ auto sendmmsg_res =
+ detail::skip_eintr([&] { return sendmmsg(native_fd, headers.data(), narrow_cast<unsigned int>(to_send), 0); });
+ auto sendmmsg_errno = errno;
+ if (sendmmsg_res >= 0) {
+ cnt = sendmmsg_res;
+ return Status::OK();
+ }
+
+ bool is_sent = false;
+ auto status = process_sendmsg_error(sendmmsg_errno, is_sent);
+ cnt = is_sent;
+ return status;
+ }
+#endif
+ Status receive_messages_slow(MutableSpan<UdpSocketFd::InboundMessage> messages, size_t &cnt) {
+ cnt = 0;
+ while (cnt < messages.size() && get_poll_info().get_flags_local().can_read()) {
+ auto &message = messages[cnt];
+ CHECK(!message.data.empty());
+ bool is_received;
+ auto error = receive_message(message, is_received);
+ cnt += is_received;
+ TRY_STATUS(std::move(error));
+ }
+ return Status::OK();
+ }
+
+#if TD_HAS_MMSG
+ Status receive_messages_fast(MutableSpan<UdpSocketFd::InboundMessage> messages, size_t &cnt) {
+ int flags = 0;
+ cnt = 0;
+ if (get_poll_info().get_flags_local().has_pending_error()) {
+#ifdef MSG_ERRQUEUE
+ flags = MSG_ERRQUEUE;
+#else
+ return get_pending_error();
+#endif
+ }
+ //struct mmsghdr {
+ // msghdr msg_hdr; [> Message header <]
+ // unsigned int msg_len; [> Number of bytes transmitted <]
+ //};
+ std::array<detail::UdpSocketReceiveHelper, 16> helpers;
+ std::array<mmsghdr, 16> headers;
+ size_t to_receive = min(messages.size(), headers.size());
+ for (size_t i = 0; i < to_receive; i++) {
+ helpers[i].to_native(messages[i], headers[i].msg_hdr);
+ headers[i].msg_len = 0;
+ }
+
+ auto native_fd = get_native_fd().socket();
+ auto recvmmsg_res = detail::skip_eintr(
+ [&] { return recvmmsg(native_fd, headers.data(), narrow_cast<unsigned int>(to_receive), flags, nullptr); });
+ auto recvmmsg_errno = errno;
+ if (recvmmsg_res >= 0) {
+ cnt = narrow_cast<size_t>(recvmmsg_res);
+ for (size_t i = 0; i < cnt; i++) {
+ UdpSocketReceiveHelper::from_native(headers[i].msg_hdr, headers[i].msg_len, messages[i]);
+ }
+ return Status::OK();
+ }
+
+ bool is_received;
+ auto status = process_recvmsg_error(recvmmsg_errno, is_received);
+ cnt = is_received;
+ return status;
+ }
+#endif
+};
+void UdpSocketFdImplDeleter::operator()(UdpSocketFdImpl *impl) {
+ delete impl;
+}
+#endif
+} // namespace detail
+
+UdpSocketFd::UdpSocketFd() = default;
+UdpSocketFd::UdpSocketFd(UdpSocketFd &&) noexcept = default;
+UdpSocketFd &UdpSocketFd::operator=(UdpSocketFd &&) noexcept = default;
+UdpSocketFd::~UdpSocketFd() = default;
+PollableFdInfo &UdpSocketFd::get_poll_info() {
+ return impl_->get_poll_info();
+}
+const PollableFdInfo &UdpSocketFd::get_poll_info() const {
+ return impl_->get_poll_info();
+}
+
+Result<UdpSocketFd> UdpSocketFd::open(const IPAddress &address) {
+ NativeFd native_fd{socket(address.get_address_family(), SOCK_DGRAM, IPPROTO_UDP)};
+ if (!native_fd) {
+ return OS_SOCKET_ERROR("Failed to create a socket");
+ }
+ TRY_STATUS(native_fd.set_is_blocking_unsafe(false));
+
+ auto sock = native_fd.socket();
+#if TD_PORT_POSIX
+ int flags = 1;
+#elif TD_PORT_WINDOWS
+ BOOL flags = TRUE;
+#endif
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ // TODO: SO_REUSEADDR, SO_KEEPALIVE, TCP_NODELAY, SO_SNDBUF, SO_RCVBUF, TCP_QUICKACK, SO_LINGER
+
+ auto bind_addr = address.get_any_addr();
+ bind_addr.set_port(address.get_port());
+ auto e_bind = bind(sock, bind_addr.get_sockaddr(), narrow_cast<int>(bind_addr.get_sockaddr_len()));
+ if (e_bind != 0) {
+ return OS_SOCKET_ERROR("Failed to bind a socket");
+ }
+ return UdpSocketFd(make_unique<detail::UdpSocketFdImpl>(std::move(native_fd)));
+}
+
+UdpSocketFd::UdpSocketFd(unique_ptr<detail::UdpSocketFdImpl> impl) : impl_(impl.release()) {
+}
+
+void UdpSocketFd::close() {
+ impl_.reset();
+}
+
+bool UdpSocketFd::empty() const {
+ return !impl_;
+}
+
+const NativeFd &UdpSocketFd::get_native_fd() const {
+ return get_poll_info().native_fd();
+}
+
+#if TD_PORT_POSIX
+static Result<uint32> maximize_buffer(int socket_fd, int optname, uint32 max_size) {
+ if (setsockopt(socket_fd, SOL_SOCKET, optname, &max_size, sizeof(max_size)) == 0) {
+ // fast path
+ return max_size;
+ }
+
+ /* Start with the default size. */
+ uint32 old_size = 0;
+ socklen_t intsize = sizeof(old_size);
+ if (getsockopt(socket_fd, SOL_SOCKET, optname, &old_size, &intsize)) {
+ return OS_ERROR("getsockopt() failed");
+ }
+#if TD_LINUX
+ old_size /= 2;
+#endif
+
+ /* Binary-search for the real maximum. */
+ uint32 last_good_size = old_size;
+ uint32 min_size = old_size;
+ while (min_size <= max_size) {
+ uint32 avg_size = min_size + (max_size - min_size) / 2;
+ if (setsockopt(socket_fd, SOL_SOCKET, optname, &avg_size, sizeof(avg_size)) == 0) {
+ last_good_size = avg_size;
+ min_size = avg_size + 1;
+ } else {
+ max_size = avg_size - 1;
+ }
+ }
+ return last_good_size;
+}
+
+Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max_size) {
+ return maximize_buffer(get_native_fd().fd(), SO_SNDBUF, max_size == 0 ? DEFAULT_UDP_MAX_SND_BUFFER_SIZE : max_size);
+}
+
+Result<uint32> UdpSocketFd::maximize_rcv_buffer(uint32 max_size) {
+ return maximize_buffer(get_native_fd().fd(), SO_RCVBUF, max_size == 0 ? DEFAULT_UDP_MAX_RCV_BUFFER_SIZE : max_size);
+}
+#else
+Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max_size) {
+ return 0;
+}
+Result<uint32> UdpSocketFd::maximize_rcv_buffer(uint32 max_size) {
+ return 0;
+}
+#endif
+
+#if TD_PORT_POSIX
+Status UdpSocketFd::send_message(const OutboundMessage &message, bool &is_sent) {
+ return impl_->send_message(message, is_sent);
+}
+Status UdpSocketFd::receive_message(InboundMessage &message, bool &is_received) {
+ return impl_->receive_message(message, is_received);
+}
+
+Status UdpSocketFd::send_messages(Span<OutboundMessage> messages, size_t &count) {
+ return impl_->send_messages(messages, count);
+}
+Status UdpSocketFd::receive_messages(MutableSpan<InboundMessage> messages, size_t &count) {
+ return impl_->receive_messages(messages, count);
+}
+#endif
+#if TD_PORT_WINDOWS
+Result<optional<UdpMessage>> UdpSocketFd::receive() {
+ return impl_->receive();
+}
+
+void UdpSocketFd::send(UdpMessage message) {
+ return impl_->send(std::move(message));
+}
+
+Status UdpSocketFd::flush_send() {
+ return impl_->flush_send();
+}
+#endif
+
+bool UdpSocketFd::is_critical_read_error(const Status &status) {
+ return status.code() == ENOMEM || status.code() == ENOBUFS;
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.h
new file mode 100644
index 0000000000..6ff5cd1491
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/UdpSocketFd.h
@@ -0,0 +1,93 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/port/config.h"
+
+#include "td/utils/buffer.h"
+#include "td/utils/optional.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
+#include "td/utils/port/IPAddress.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Span.h"
+#include "td/utils/Status.h"
+
+#include <memory>
+
+namespace td {
+// Udp and errors
+namespace detail {
+class UdpSocketFdImpl;
+class UdpSocketFdImplDeleter {
+ public:
+ void operator()(UdpSocketFdImpl *impl);
+};
+} // namespace detail
+
+struct UdpMessage {
+ IPAddress address;
+ BufferSlice data;
+ Status error;
+};
+
+class UdpSocketFd {
+ public:
+ UdpSocketFd();
+ UdpSocketFd(UdpSocketFd &&) noexcept;
+ UdpSocketFd &operator=(UdpSocketFd &&) noexcept;
+ ~UdpSocketFd();
+
+ UdpSocketFd(const UdpSocketFd &) = delete;
+ UdpSocketFd &operator=(const UdpSocketFd &) = delete;
+
+ Result<uint32> maximize_snd_buffer(uint32 max_size = 0);
+ Result<uint32> maximize_rcv_buffer(uint32 max_size = 0);
+
+ static Result<UdpSocketFd> open(const IPAddress &address) TD_WARN_UNUSED_RESULT;
+
+ PollableFdInfo &get_poll_info();
+ const PollableFdInfo &get_poll_info() const;
+ const NativeFd &get_native_fd() const;
+
+ void close();
+ bool empty() const;
+
+ static bool is_critical_read_error(const Status &status);
+
+#if TD_PORT_POSIX
+ struct OutboundMessage {
+ const IPAddress *to;
+ Slice data;
+ };
+ struct InboundMessage {
+ IPAddress *from;
+ MutableSlice data;
+ Status *error;
+ };
+
+ Status send_message(const OutboundMessage &message, bool &is_sent) TD_WARN_UNUSED_RESULT;
+ Status receive_message(InboundMessage &message, bool &is_received) TD_WARN_UNUSED_RESULT;
+
+ Status send_messages(Span<OutboundMessage> messages, size_t &count) TD_WARN_UNUSED_RESULT;
+ Status receive_messages(MutableSpan<InboundMessage> messages, size_t &count) TD_WARN_UNUSED_RESULT;
+#elif TD_PORT_WINDOWS
+ Result<optional<UdpMessage> > receive();
+
+ void send(UdpMessage message);
+
+ Status flush_send();
+#endif
+
+ private:
+ static constexpr uint32 DEFAULT_UDP_MAX_SND_BUFFER_SIZE = (1 << 24);
+ static constexpr uint32 DEFAULT_UDP_MAX_RCV_BUFFER_SIZE = (1 << 24);
+ std::unique_ptr<detail::UdpSocketFdImpl, detail::UdpSocketFdImplDeleter> impl_;
+ explicit UdpSocketFd(unique_ptr<detail::UdpSocketFdImpl> impl);
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/config.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/config.h
index 0ffdb3c3bf..59fd94bf00 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/config.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/config.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -19,6 +19,9 @@
#if TD_LINUX || TD_ANDROID || TD_TIZEN
#define TD_POLL_EPOLL 1
#define TD_EVENTFD_LINUX 1
+#elif TD_FREEBSD || TD_OPENBSD || TD_NETBSD
+ #define TD_POLL_KQUEUE 1
+ #define TD_EVENTFD_BSD 1
#elif TD_CYGWIN
#define TD_POLL_SELECT 1
#define TD_EVENTFD_BSD 1
@@ -31,16 +34,26 @@
#elif TD_WINDOWS
#define TD_POLL_WINEVENT 1
#define TD_EVENTFD_WINDOWS 1
+#elif TD_ILLUMOS
+ #define TD_POLL_EPOLL 1
+ #define TD_EVENTFD_LINUX 1
+#elif TD_SOLARIS
+ #define TD_POLL_POLL 1
+ #define TD_EVENTFD_BSD 1
#else
#error "Poll's implementation is not defined"
#endif
#if TD_EMSCRIPTEN
#define TD_THREAD_UNSUPPORTED 1
-#elif TD_TIZEN
- #define TD_THREAD_PTHREAD 1
-#else
+#elif TD_WINDOWS
#define TD_THREAD_STL 1
+#else
+ #define TD_THREAD_PTHREAD 1
+#endif
+
+#if TD_LINUX
+ #define TD_HAS_MMSG 1
#endif
// clang-format on
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.cpp
index 2ef026d164..771b06a954 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,98 +14,110 @@ char disable_linker_warning_about_empty_file_epoll_cpp TD_UNUSED;
#include "td/utils/logging.h"
#include "td/utils/Status.h"
+#include <cerrno>
+
#include <unistd.h>
namespace td {
namespace detail {
void Epoll::init() {
- CHECK(epoll_fd == -1);
- epoll_fd = epoll_create(1);
+ CHECK(!epoll_fd_);
+ epoll_fd_ = NativeFd(epoll_create(1));
auto epoll_create_errno = errno;
- LOG_IF(FATAL, epoll_fd == -1) << Status::PosixError(epoll_create_errno, "epoll_create failed");
+ LOG_IF(FATAL, !epoll_fd_) << Status::PosixError(epoll_create_errno, "epoll_create failed");
- events.resize(1000);
+ events_.resize(1000);
}
void Epoll::clear() {
- if (epoll_fd == -1) {
+ if (!epoll_fd_) {
return;
}
- events.clear();
+ events_.clear();
+
+ epoll_fd_.close();
- close(epoll_fd);
- epoll_fd = -1;
+ for (auto *list_node = list_root_.next; list_node != &list_root_;) {
+ auto pollable_fd = PollableFd::from_list_node(list_node);
+ list_node = list_node->next;
+ }
}
-void Epoll::subscribe(const Fd &fd, Fd::Flags flags) {
+void Epoll::subscribe(PollableFd fd, PollFlags flags) {
epoll_event event;
event.events = EPOLLHUP | EPOLLERR | EPOLLET;
#ifdef EPOLLRDHUP
event.events |= EPOLLRDHUP;
#endif
- if (flags & Fd::Read) {
+ if (flags.can_read()) {
event.events |= EPOLLIN;
}
- if (flags & Fd::Write) {
+ if (flags.can_write()) {
event.events |= EPOLLOUT;
}
- auto native_fd = fd.get_native_fd();
- event.data.fd = native_fd;
- int err = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, native_fd, &event);
+ auto native_fd = fd.native_fd().fd();
+ auto *list_node = fd.release_as_list_node();
+ list_root_.put(list_node);
+ event.data.ptr = list_node;
+
+ int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_ADD, native_fd, &event);
auto epoll_ctl_errno = errno;
- LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl ADD failed") << ", epoll_fd = " << epoll_fd
- << ", fd = " << native_fd;
+ LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl ADD failed")
+ << ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd;
}
-void Epoll::unsubscribe(const Fd &fd) {
- auto native_fd = fd.get_native_fd();
- int err = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, native_fd, nullptr);
+void Epoll::unsubscribe(PollableFdRef fd_ref) {
+ auto fd = fd_ref.lock();
+ auto native_fd = fd.native_fd().fd();
+ int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_DEL, native_fd, nullptr);
auto epoll_ctl_errno = errno;
- LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl DEL failed") << ", epoll_fd = " << epoll_fd
- << ", fd = " << native_fd;
+ LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl DEL failed")
+ << ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd
+ << ", status = " << fd.native_fd().validate();
}
-void Epoll::unsubscribe_before_close(const Fd &fd) {
+void Epoll::unsubscribe_before_close(PollableFdRef fd) {
unsubscribe(fd);
}
void Epoll::run(int timeout_ms) {
- int ready_n = epoll_wait(epoll_fd, &events[0], static_cast<int>(events.size()), timeout_ms);
+ int ready_n = epoll_wait(epoll_fd_.fd(), &events_[0], static_cast<int>(events_.size()), timeout_ms);
auto epoll_wait_errno = errno;
LOG_IF(FATAL, ready_n == -1 && epoll_wait_errno != EINTR)
<< Status::PosixError(epoll_wait_errno, "epoll_wait failed");
for (int i = 0; i < ready_n; i++) {
- Fd::Flags flags = 0;
- epoll_event *event = &events[i];
+ PollFlags flags;
+ epoll_event *event = &events_[i];
if (event->events & EPOLLIN) {
event->events &= ~EPOLLIN;
- flags |= Fd::Read;
+ flags = flags | PollFlags::Read();
}
if (event->events & EPOLLOUT) {
event->events &= ~EPOLLOUT;
- flags |= Fd::Write;
+ flags = flags | PollFlags::Write();
}
#ifdef EPOLLRDHUP
if (event->events & EPOLLRDHUP) {
event->events &= ~EPOLLRDHUP;
- // flags |= Fd::Close;
- // TODO
+ flags = flags | PollFlags::Close();
}
#endif
if (event->events & EPOLLHUP) {
event->events &= ~EPOLLHUP;
- flags |= Fd::Close;
+ flags = flags | PollFlags::Close();
}
if (event->events & EPOLLERR) {
event->events &= ~EPOLLERR;
- flags |= Fd::Error;
+ flags = flags | PollFlags::Error();
}
if (event->events) {
- LOG(FATAL) << "Unsupported epoll events: " << event->events;
+ LOG(FATAL) << "Unsupported epoll events: " << static_cast<int32>(event->events);
}
- // LOG(DEBUG) << "Epoll event " << tag("fd", event->data.fd) << tag("flags", format::as_binary(flags));
- Fd(event->data.fd, Fd::Mode::Reference).update_flags_notify(flags);
+ //LOG(DEBUG) << "Epoll event " << tag("fd", event->data.fd) << tag("flags", format::as_binary(flags));
+ auto pollable_fd = PollableFd::from_list_node(static_cast<ListNode *>(event->data.ptr));
+ pollable_fd.add_flags(flags);
+ pollable_fd.release_as_list_node();
}
}
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.h
index db4f66e5a7..da02de7c7b 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Epoll.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,8 +11,11 @@
#ifdef TD_POLL_EPOLL
#include "td/utils/common.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/List.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
+#include "td/utils/port/PollFlags.h"
#include <sys/epoll.h>
@@ -26,23 +29,28 @@ class Epoll final : public PollBase {
Epoll &operator=(const Epoll &) = delete;
Epoll(Epoll &&) = delete;
Epoll &operator=(Epoll &&) = delete;
- ~Epoll() override = default;
+ ~Epoll() final = default;
- void init() override;
+ void init() final;
- void clear() override;
+ void clear() final;
- void subscribe(const Fd &fd, Fd::Flags flags) override;
+ void subscribe(PollableFd fd, PollFlags flags) final;
- void unsubscribe(const Fd &fd) override;
+ void unsubscribe(PollableFdRef fd) final;
- void unsubscribe_before_close(const Fd &fd) override;
+ void unsubscribe_before_close(PollableFdRef fd) final;
- void run(int timeout_ms) override;
+ void run(int timeout_ms) final;
+
+ static bool is_edge_triggered() {
+ return true;
+ }
private:
- int epoll_fd = -1;
- vector<struct epoll_event> events;
+ NativeFd epoll_fd_;
+ vector<struct epoll_event> events_;
+ ListNode list_root_;
};
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.cpp
index d51e99ac0a..8f22aeb5c1 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,9 +11,16 @@ char disable_linker_warning_about_empty_file_event_fd_bsd_cpp TD_UNUSED;
#ifdef TD_EVENTFD_BSD
#include "td/utils/logging.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/skip_eintr.h"
+#include "td/utils/port/PollFlags.h"
+#include "td/utils/port/SocketFd.h"
#include "td/utils/Slice.h"
+#include <cerrno>
+
#include <fcntl.h>
+#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -36,11 +43,13 @@ void EventFdBsd::init() {
#endif
LOG_IF(FATAL, err == -1) << Status::PosixError(socketpair_errno, "socketpair failed");
- detail::set_native_socket_is_blocking(fds[0], false).ensure();
- detail::set_native_socket_is_blocking(fds[1], false).ensure();
+ auto fd_a = NativeFd(fds[0]);
+ auto fd_b = NativeFd(fds[1]);
+ fd_a.set_is_blocking_unsafe(false).ensure();
+ fd_b.set_is_blocking_unsafe(false).ensure();
- in_ = Fd(fds[0], Fd::Mode::Owner);
- out_ = Fd(fds[1], Fd::Mode::Owner);
+ in_ = SocketFd::from_native_fd(std::move(fd_a)).move_as_ok();
+ out_ = SocketFd::from_native_fd(std::move(fd_b)).move_as_ok();
}
bool EventFdBsd::empty() {
@@ -56,12 +65,8 @@ Status EventFdBsd::get_pending_error() {
return Status::OK();
}
-const Fd &EventFdBsd::get_fd() const {
- return out_;
-}
-
-Fd &EventFdBsd::get_fd() {
- return out_;
+PollableFdInfo &EventFdBsd::get_poll_info() {
+ return out_.get_poll_info();
}
void EventFdBsd::release() {
@@ -72,13 +77,14 @@ void EventFdBsd::release() {
}
size_t size = result.ok();
if (size != sizeof(value)) {
- LOG(FATAL) << "EventFdBsd write returned " << value << " instead of " << sizeof(value);
+ LOG(FATAL) << "EventFdBsd write returned " << size << " instead of " << sizeof(value);
}
}
void EventFdBsd::acquire() {
- out_.update_flags(Fd::Read);
- while (can_read(out_)) {
+ sync_with_poll(out_);
+ out_.get_poll_info().add_flags(PollFlags::Read());
+ while (can_read_local(out_)) {
uint8 value[1024];
auto result = out_.read(MutableSlice(value, sizeof(value)));
if (result.is_error()) {
@@ -87,6 +93,17 @@ void EventFdBsd::acquire() {
}
}
+void EventFdBsd::wait(int timeout_ms) {
+ detail::skip_eintr_timeout(
+ [this](int timeout_ms) {
+ pollfd fd;
+ fd.fd = get_poll_info().native_fd().fd();
+ fd.events = POLLIN;
+ return poll(&fd, 1, timeout_ms);
+ },
+ timeout_ms);
+}
+
} // namespace detail
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.h
index 08f7ddd308..5278f74b23 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdBsd.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,34 +11,36 @@
#ifdef TD_EVENTFD_BSD
#include "td/utils/common.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/EventFdBase.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/SocketFd.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
class EventFdBsd final : public EventFdBase {
- Fd in_;
- Fd out_;
+ SocketFd in_;
+ SocketFd out_;
public:
EventFdBsd() = default;
- void init() override;
+ void init() final;
- bool empty() override;
+ bool empty() final;
- void close() override;
+ void close() final;
- Status get_pending_error() override TD_WARN_UNUSED_RESULT;
+ Status get_pending_error() final TD_WARN_UNUSED_RESULT;
- const Fd &get_fd() const override;
- Fd &get_fd() override;
+ PollableFdInfo &get_poll_info() final;
- void release() override;
+ void release() final;
- void acquire() override;
+ void acquire() final;
+
+ void wait(int timeout_ms) final;
};
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.cpp
index fd08c9af08..bcab560af7 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,45 +11,71 @@ char disable_linker_warning_about_empty_file_event_fd_linux_cpp TD_UNUSED;
#ifdef TD_EVENTFD_LINUX
#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/skip_eintr.h"
+#include "td/utils/port/PollFlags.h"
+#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+#include <cerrno>
+
+#include <poll.h>
#include <sys/eventfd.h>
+#include <unistd.h>
namespace td {
namespace detail {
+class EventFdLinuxImpl {
+ public:
+ PollableFdInfo info_;
+};
+
+EventFdLinux::EventFdLinux() = default;
+EventFdLinux::EventFdLinux(EventFdLinux &&) noexcept = default;
+EventFdLinux &EventFdLinux::operator=(EventFdLinux &&) noexcept = default;
+EventFdLinux::~EventFdLinux() = default;
void EventFdLinux::init() {
- int fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+ auto fd = NativeFd(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
auto eventfd_errno = errno;
- LOG_IF(FATAL, fd == -1) << Status::PosixError(eventfd_errno, "eventfd call failed");
-
- fd_ = Fd(fd, Fd::Mode::Owner);
+ LOG_IF(FATAL, !fd) << Status::PosixError(eventfd_errno, "eventfd call failed");
+ impl_ = make_unique<EventFdLinuxImpl>();
+ impl_->info_.set_native_fd(std::move(fd));
}
bool EventFdLinux::empty() {
- return fd_.empty();
+ return !impl_;
}
void EventFdLinux::close() {
- fd_.close();
+ impl_.reset();
}
Status EventFdLinux::get_pending_error() {
return Status::OK();
}
-const Fd &EventFdLinux::get_fd() const {
- return fd_;
-}
-
-Fd &EventFdLinux::get_fd() {
- return fd_;
+PollableFdInfo &EventFdLinux::get_poll_info() {
+ return impl_->info_;
}
+// NB: will be called from multiple threads
void EventFdLinux::release() {
const uint64 value = 1;
- // NB: write_unsafe is used, because release will be called from multiple threads
- auto result = fd_.write_unsafe(Slice(reinterpret_cast<const char *>(&value), sizeof(value)));
+ auto slice = Slice(reinterpret_cast<const char *>(&value), sizeof(value));
+ auto native_fd = impl_->info_.native_fd().fd();
+
+ auto result = [&]() -> Result<size_t> {
+ auto write_res = detail::skip_eintr([&] { return write(native_fd, slice.begin(), slice.size()); });
+ auto write_errno = errno;
+ if (write_res >= 0) {
+ return narrow_cast<size_t>(write_res);
+ }
+ return Status::PosixError(write_errno, PSLICE() << "Write to fd " << native_fd << " has failed");
+ }();
+
if (result.is_error()) {
LOG(FATAL) << "EventFdLinux write failed: " << result.error();
}
@@ -60,12 +86,46 @@ void EventFdLinux::release() {
}
void EventFdLinux::acquire() {
+ impl_->info_.sync_with_poll();
+ SCOPE_EXIT {
+ // Clear flags without EAGAIN and EWOULDBLOCK
+ // Looks like it is safe thing to do with eventfd
+ get_poll_info().clear_flags(PollFlags::Read());
+ };
uint64 res;
- auto result = fd_.read(MutableSlice(reinterpret_cast<char *>(&res), sizeof(res)));
+ auto slice = MutableSlice(reinterpret_cast<char *>(&res), sizeof(res));
+ auto native_fd = impl_->info_.native_fd().fd();
+ auto result = [&]() -> Result<size_t> {
+ CHECK(!slice.empty());
+ auto read_res = detail::skip_eintr([&] { return ::read(native_fd, slice.begin(), slice.size()); });
+ auto read_errno = errno;
+ if (read_res >= 0) {
+ CHECK(read_res != 0);
+ return narrow_cast<size_t>(read_res);
+ }
+ if (read_errno == EAGAIN
+#if EAGAIN != EWOULDBLOCK
+ || read_errno == EWOULDBLOCK
+#endif
+ ) {
+ return 0;
+ }
+ return Status::PosixError(read_errno, PSLICE() << "Read from fd " << native_fd << " has failed");
+ }();
if (result.is_error()) {
LOG(FATAL) << "EventFdLinux read failed: " << result.error();
}
- fd_.clear_flags(Fd::Read);
+}
+
+void EventFdLinux::wait(int timeout_ms) {
+ detail::skip_eintr_timeout(
+ [this](int timeout_ms) {
+ pollfd fd;
+ fd.fd = get_poll_info().native_fd().fd();
+ fd.events = POLLIN;
+ return poll(&fd, 1, timeout_ms);
+ },
+ timeout_ms);
}
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.h
index 3df7ce3a5d..2cd4d12210 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdLinux.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,31 +11,38 @@
#ifdef TD_EVENTFD_LINUX
#include "td/utils/common.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/EventFdBase.h"
-#include "td/utils/port/Fd.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
+class EventFdLinuxImpl;
class EventFdLinux final : public EventFdBase {
- Fd fd_;
+ unique_ptr<EventFdLinuxImpl> impl_;
public:
- void init() override;
+ EventFdLinux();
+ EventFdLinux(EventFdLinux &&) noexcept;
+ EventFdLinux &operator=(EventFdLinux &&) noexcept;
+ ~EventFdLinux() final;
- bool empty() override;
+ void init() final;
- void close() override;
+ bool empty() final;
- Status get_pending_error() override TD_WARN_UNUSED_RESULT;
+ void close() final;
- const Fd &get_fd() const override;
- Fd &get_fd() override;
+ Status get_pending_error() final TD_WARN_UNUSED_RESULT;
- void release() override;
+ PollableFdInfo &get_poll_info() final;
- void acquire() override;
+ void release() final;
+
+ void acquire() final;
+
+ void wait(int timeout_ms) final;
};
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.cpp
index 8adfd5a686..7acaa3f17b 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,39 +10,56 @@ char disable_linker_warning_about_empty_file_event_fd_windows_cpp TD_UNUSED;
#ifdef TD_EVENTFD_WINDOWS
+#include "td/utils/logging.h"
+
namespace td {
namespace detail {
void EventFdWindows::init() {
- fd_ = Fd::create_event_fd();
+ auto handle = CreateEventW(nullptr, true, false, nullptr);
+ if (handle == nullptr) {
+ auto error = OS_ERROR("CreateEventW failed");
+ LOG(FATAL) << error;
+ }
+ event_ = NativeFd(handle);
}
bool EventFdWindows::empty() {
- return fd_.empty();
+ return !event_;
}
void EventFdWindows::close() {
- fd_.close();
+ event_.close();
}
Status EventFdWindows::get_pending_error() {
return Status::OK();
}
-const Fd &EventFdWindows::get_fd() const {
- return fd_;
-}
-
-Fd &EventFdWindows::get_fd() {
- return fd_;
+PollableFdInfo &EventFdWindows::get_poll_info() {
+ UNREACHABLE();
}
void EventFdWindows::release() {
- fd_.release();
+ if (SetEvent(event_.fd()) == 0) {
+ auto error = OS_ERROR("SetEvent failed");
+ LOG(FATAL) << error;
+ }
}
void EventFdWindows::acquire() {
- fd_.acquire();
+ if (ResetEvent(event_.fd()) == 0) {
+ auto error = OS_ERROR("ResetEvent failed");
+ LOG(FATAL) << error;
+ }
+}
+
+void EventFdWindows::wait(int timeout_ms) {
+ WaitForSingleObject(event_.fd(), timeout_ms);
+ if (ResetEvent(event_.fd()) == 0) {
+ auto error = OS_ERROR("ResetEvent failed");
+ LOG(FATAL) << error;
+ }
}
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.h
index 48e1c763b3..5794e6c7ca 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/EventFdWindows.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,33 +11,35 @@
#ifdef TD_EVENTFD_WINDOWS
#include "td/utils/common.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/EventFdBase.h"
-#include "td/utils/port/Fd.h"
#include "td/utils/Status.h"
namespace td {
namespace detail {
class EventFdWindows final : public EventFdBase {
- Fd fd_;
+ NativeFd event_;
public:
EventFdWindows() = default;
- void init() override;
+ void init() final;
- bool empty() override;
+ bool empty() final;
- void close() override;
+ void close() final;
- Status get_pending_error() override TD_WARN_UNUSED_RESULT;
+ Status get_pending_error() final TD_WARN_UNUSED_RESULT;
- const Fd &get_fd() const override;
- Fd &get_fd() override;
+ PollableFdInfo &get_poll_info() final;
- void release() override;
+ void release() final;
- void acquire() override;
+ void acquire() final;
+
+ void wait(int timeout_ms) final;
};
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.cpp
new file mode 100644
index 0000000000..1e472df67f
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.cpp
@@ -0,0 +1,110 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/detail/Iocp.h"
+
+char disable_linker_warning_about_empty_file_iocp_cpp TD_UNUSED;
+
+#ifdef TD_PORT_WINDOWS
+
+#include "td/utils/logging.h"
+
+namespace td {
+namespace detail {
+Iocp::~Iocp() {
+ clear();
+}
+
+void Iocp::loop() {
+ Iocp::Guard guard(this);
+ while (true) {
+ DWORD bytes = 0;
+ ULONG_PTR key = 0;
+ WSAOVERLAPPED *overlapped = nullptr;
+ BOOL ok =
+ GetQueuedCompletionStatus(iocp_handle_->fd(), &bytes, &key, reinterpret_cast<OVERLAPPED **>(&overlapped), 1000);
+ if (bytes || key || overlapped) {
+ // LOG(ERROR) << "Got IOCP " << bytes << " " << key << " " << overlapped;
+ }
+ if (ok) {
+ auto callback = reinterpret_cast<Iocp::Callback *>(key);
+ if (callback == nullptr) {
+ // LOG(ERROR) << "Interrupt IOCP loop";
+ return;
+ }
+ callback->on_iocp(bytes, overlapped);
+ } else {
+ if (overlapped != nullptr) {
+ auto error = OS_ERROR("Received from IOCP");
+ auto callback = reinterpret_cast<Iocp::Callback *>(key);
+ CHECK(callback != nullptr);
+ callback->on_iocp(std::move(error), overlapped);
+ }
+ }
+ }
+}
+
+void Iocp::interrupt_loop() {
+ post(0, nullptr, nullptr);
+}
+
+void Iocp::init() {
+ CHECK(!iocp_handle_);
+ auto res = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
+ if (res == nullptr) {
+ auto error = OS_ERROR("IOCP creation failed");
+ LOG(FATAL) << error;
+ }
+ iocp_handle_ = std::make_shared<NativeFd>(res);
+}
+
+void Iocp::clear() {
+ iocp_handle_.reset();
+}
+
+void Iocp::subscribe(const NativeFd &native_fd, Callback *callback) {
+ CHECK(iocp_handle_);
+ auto iocp_handle =
+ CreateIoCompletionPort(native_fd.fd(), iocp_handle_->fd(), reinterpret_cast<ULONG_PTR>(callback), 0);
+ if (iocp_handle == nullptr) {
+ auto error = OS_ERROR("CreateIoCompletionPort");
+ LOG(FATAL) << error;
+ }
+ LOG_CHECK(iocp_handle == iocp_handle_->fd()) << iocp_handle << " " << iocp_handle_->fd();
+}
+
+IocpRef Iocp::get_ref() const {
+ return IocpRef(iocp_handle_);
+}
+
+static void iocp_post(NativeFd &iocp_handle, size_t size, Iocp::Callback *callback, WSAOVERLAPPED *overlapped) {
+ if (PostQueuedCompletionStatus(iocp_handle.fd(), DWORD(size), reinterpret_cast<ULONG_PTR>(callback),
+ reinterpret_cast<OVERLAPPED *>(overlapped)) == 0) {
+ auto error = OS_ERROR("IOCP post failed");
+ LOG(FATAL) << error;
+ }
+}
+
+void Iocp::post(size_t size, Callback *callback, WSAOVERLAPPED *overlapped) {
+ iocp_post(*iocp_handle_, size, callback, overlapped);
+}
+
+IocpRef::IocpRef(std::weak_ptr<NativeFd> iocp_handle) : iocp_handle_(std::move(iocp_handle)) {
+}
+
+bool IocpRef::post(size_t size, Iocp::Callback *callback, WSAOVERLAPPED *overlapped) {
+ auto iocp_handle = iocp_handle_.lock();
+ if (!iocp_handle) {
+ return false;
+ }
+ iocp_post(*iocp_handle, size, callback, overlapped);
+ return true;
+}
+
+} // namespace detail
+} // namespace td
+
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.h
new file mode 100644
index 0000000000..a37cce7e57
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Iocp.h
@@ -0,0 +1,71 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/port/config.h"
+
+#ifdef TD_PORT_WINDOWS
+
+#include "td/utils/common.h"
+#include "td/utils/Context.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/Status.h"
+
+#include <memory>
+
+namespace td {
+namespace detail {
+
+class IocpRef;
+class Iocp final : public Context<Iocp> {
+ public:
+ Iocp() = default;
+ Iocp(const Iocp &) = delete;
+ Iocp &operator=(const Iocp &) = delete;
+ Iocp(Iocp &&) = delete;
+ Iocp &operator=(Iocp &&) = delete;
+ ~Iocp();
+
+ class Callback {
+ public:
+ virtual ~Callback() = default;
+ virtual void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) = 0;
+ };
+
+ void init();
+ void subscribe(const NativeFd &fd, Callback *callback);
+ void post(size_t size, Callback *callback, WSAOVERLAPPED *overlapped);
+ void loop();
+ void interrupt_loop();
+ void clear();
+
+ IocpRef get_ref() const;
+
+ private:
+ std::shared_ptr<NativeFd> iocp_handle_;
+};
+
+class IocpRef {
+ public:
+ IocpRef() = default;
+ IocpRef(const Iocp &) = delete;
+ IocpRef &operator=(const Iocp &) = delete;
+ IocpRef(IocpRef &&) = default;
+ IocpRef &operator=(IocpRef &&) = default;
+
+ explicit IocpRef(std::weak_ptr<NativeFd> iocp_handle);
+
+ bool post(size_t size, Iocp::Callback *callback, WSAOVERLAPPED *overlapped);
+
+ private:
+ std::weak_ptr<NativeFd> iocp_handle_;
+};
+
+} // namespace detail
+} // namespace td
+
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.cpp
index 351f8d7d6c..64c3c54302 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -13,54 +13,58 @@ char disable_linker_warning_about_empty_file_kqueue_cpp TD_UNUSED;
#include "td/utils/logging.h"
#include "td/utils/Status.h"
+#include <cerrno>
#include <utility>
+#include <sys/time.h>
#include <unistd.h>
namespace td {
namespace detail {
-KQueue::KQueue() {
- kq = -1;
-}
KQueue::~KQueue() {
clear();
}
void KQueue::init() {
- kq = kqueue();
+ kq_ = NativeFd(kqueue());
auto kqueue_errno = errno;
- LOG_IF(FATAL, kq == -1) << Status::PosixError(kqueue_errno, "kqueue creation failed");
+ LOG_IF(FATAL, !kq_) << Status::PosixError(kqueue_errno, "kqueue creation failed");
// TODO: const
- events.resize(1000);
- changes_n = 0;
+ events_.resize(1000);
+ changes_n_ = 0;
}
void KQueue::clear() {
- if (kq == -1) {
+ if (!kq_) {
return;
}
- events.clear();
- close(kq);
- kq = -1;
+ events_.clear();
+ kq_.close();
+ for (auto *list_node = list_root_.next; list_node != &list_root_;) {
+ auto pollable_fd = PollableFd::from_list_node(list_node);
+ list_node = list_node->next;
+ }
}
int KQueue::update(int nevents, const timespec *timeout, bool may_fail) {
- int err = kevent(kq, &events[0], changes_n, &events[0], nevents, timeout);
+ int err = kevent(kq_.fd(), &events_[0], changes_n_, &events_[0], nevents, timeout);
auto kevent_errno = errno;
bool is_fatal_error = [&] {
if (err != -1) {
return false;
}
- if (may_fail) {
- return kevent_errno != ENOENT;
+ if (may_fail && kevent_errno == ENOENT) {
+ return false;
}
return kevent_errno != EINTR;
}();
- LOG_IF(FATAL, is_fatal_error) << Status::PosixError(kevent_errno, "kevent failed");
+ if (is_fatal_error) {
+ LOG(FATAL) << Status::PosixError(kevent_errno, "kevent failed");
+ }
- changes_n = 0;
+ changes_n_ = 0;
if (err < 0) {
return 0;
}
@@ -68,7 +72,7 @@ int KQueue::update(int nevents, const timespec *timeout, bool may_fail) {
}
void KQueue::flush_changes(bool may_fail) {
- if (!changes_n) {
+ if (!changes_n_) {
return;
}
int n = update(0, nullptr, may_fail);
@@ -77,47 +81,59 @@ void KQueue::flush_changes(bool may_fail) {
void KQueue::add_change(std::uintptr_t ident, int16 filter, uint16 flags, uint32 fflags, std::intptr_t data,
void *udata) {
- if (changes_n == static_cast<int>(events.size())) {
+ if (changes_n_ == static_cast<int>(events_.size())) {
flush_changes();
}
- EV_SET(&events[changes_n], ident, filter, flags, fflags, data, udata);
+#if TD_NETBSD
+ auto set_udata = reinterpret_cast<std::intptr_t>(udata);
+#else
+ auto set_udata = udata;
+#endif
+ EV_SET(&events_[changes_n_], ident, filter, flags, fflags, data, set_udata);
VLOG(fd) << "Subscribe [fd:" << ident << "] [filter:" << filter << "] [udata: " << udata << "]";
- changes_n++;
+ changes_n_++;
}
-void KQueue::subscribe(const Fd &fd, Fd::Flags flags) {
- if (flags & Fd::Read) {
- add_change(fd.get_native_fd(), EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, nullptr);
+void KQueue::subscribe(PollableFd fd, PollFlags flags) {
+ auto native_fd = fd.native_fd().fd();
+ auto list_node = fd.release_as_list_node();
+ list_root_.put(list_node);
+ if (flags.can_read()) {
+ add_change(native_fd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, list_node);
}
- if (flags & Fd::Write) {
- add_change(fd.get_native_fd(), EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, nullptr);
+ if (flags.can_write()) {
+ add_change(native_fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, list_node);
}
}
-void KQueue::invalidate(const Fd &fd) {
- for (int i = 0; i < changes_n; i++) {
- if (events[i].ident == static_cast<std::uintptr_t>(fd.get_native_fd())) {
- changes_n--;
- std::swap(events[i], events[changes_n]);
+void KQueue::invalidate(int native_fd) {
+ for (int i = 0; i < changes_n_; i++) {
+ if (events_[i].ident == static_cast<std::uintptr_t>(native_fd)) {
+ changes_n_--;
+ std::swap(events_[i], events_[changes_n_]);
i--;
}
}
}
-void KQueue::unsubscribe(const Fd &fd) {
+void KQueue::unsubscribe(PollableFdRef fd_ref) {
+ auto pollable_fd = fd_ref.lock();
+ auto native_fd = pollable_fd.native_fd().fd();
+
// invalidate(fd);
flush_changes();
- add_change(fd.get_native_fd(), EVFILT_READ, EV_DELETE, 0, 0, nullptr);
+ add_change(native_fd, EVFILT_READ, EV_DELETE, 0, 0, nullptr);
flush_changes(true);
- add_change(fd.get_native_fd(), EVFILT_WRITE, EV_DELETE, 0, 0, nullptr);
+ add_change(native_fd, EVFILT_WRITE, EV_DELETE, 0, 0, nullptr);
flush_changes(true);
}
-void KQueue::unsubscribe_before_close(const Fd &fd) {
- invalidate(fd);
+void KQueue::unsubscribe_before_close(PollableFdRef fd_ref) {
+ auto pollable_fd = fd_ref.lock();
+ invalidate(pollable_fd.native_fd().fd());
// just to avoid O(changes_n ^ 2)
- if (changes_n != 0) {
+ if (changes_n_ != 0) {
flush_changes();
}
}
@@ -133,25 +149,32 @@ void KQueue::run(int timeout_ms) {
timeout_ptr = &timeout_data;
}
- int n = update(static_cast<int>(events.size()), timeout_ptr);
+ int n = update(static_cast<int>(events_.size()), timeout_ptr);
for (int i = 0; i < n; i++) {
- struct kevent *event = &events[i];
- Fd::Flags flags = 0;
+ struct kevent *event = &events_[i];
+ PollFlags flags;
if (event->filter == EVFILT_WRITE) {
- flags |= Fd::Write;
+ flags.add_flags(PollFlags::Write());
}
if (event->filter == EVFILT_READ) {
- flags |= Fd::Read;
+ flags.add_flags(PollFlags::Read());
}
if (event->flags & EV_EOF) {
- flags |= Fd::Close;
+ flags.add_flags(PollFlags::Close());
}
if (event->fflags & EV_ERROR) {
LOG(FATAL) << "EV_ERROR in kqueue is not supported";
}
- VLOG(fd) << "Event [fd:" << event->ident << "] [filter:" << event->filter << "] [udata: " << event->udata << "]";
- // LOG(WARNING) << "event->ident = " << event->ident << "event->filter = " << event->filter;
- Fd(static_cast<int>(event->ident), Fd::Mode::Reference).update_flags_notify(flags);
+#if TD_NETBSD
+ auto udata = reinterpret_cast<void *>(event->udata);
+#else
+ auto udata = event->udata;
+#endif
+ VLOG(fd) << "Event [fd:" << event->ident << "] [filter:" << event->filter << "] [udata: " << udata << "]";
+ // LOG(WARNING) << "Have event->ident = " << event->ident << "event->filter = " << event->filter;
+ auto pollable_fd = PollableFd::from_list_node(static_cast<ListNode *>(udata));
+ pollable_fd.add_flags(flags);
+ pollable_fd.release_as_list_node();
}
}
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.h
index e1b71e5fa5..d74443c475 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/KQueue.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,11 +11,16 @@
#ifdef TD_POLL_KQUEUE
#include "td/utils/common.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/List.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
+#include "td/utils/port/PollFlags.h"
#include <cstdint>
+#include <sys/types.h> // must be included before sys/event.h, which depends on sys/types.h on FreeBSD
+
#include <sys/event.h>
namespace td {
@@ -23,33 +28,38 @@ namespace detail {
class KQueue final : public PollBase {
public:
- KQueue();
+ KQueue() = default;
KQueue(const KQueue &) = delete;
KQueue &operator=(const KQueue &) = delete;
KQueue(KQueue &&) = delete;
KQueue &operator=(KQueue &&) = delete;
- ~KQueue() override;
+ ~KQueue() final;
+
+ void init() final;
- void init() override;
+ void clear() final;
- void clear() override;
+ void subscribe(PollableFd fd, PollFlags flags) final;
- void subscribe(const Fd &fd, Fd::Flags flags) override;
+ void unsubscribe(PollableFdRef fd) final;
- void unsubscribe(const Fd &fd) override;
+ void unsubscribe_before_close(PollableFdRef fd) final;
- void unsubscribe_before_close(const Fd &fd) override;
+ void run(int timeout_ms) final;
- void run(int timeout_ms) override;
+ static bool is_edge_triggered() {
+ return true;
+ }
private:
- vector<struct kevent> events;
- int changes_n;
- int kq;
+ vector<struct kevent> events_;
+ int changes_n_;
+ NativeFd kq_;
+ ListNode list_root_;
int update(int nevents, const timespec *timeout, bool may_fail = false);
- void invalidate(const Fd &fd);
+ void invalidate(int native_fd);
void flush_changes(bool may_fail = false);
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.cpp
new file mode 100644
index 0000000000..7a492857ec
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.cpp
@@ -0,0 +1,261 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/detail/NativeFd.h"
+
+#include "td/utils/format.h"
+#include "td/utils/logging.h"
+#include "td/utils/SliceBuilder.h"
+
+#if TD_PORT_POSIX
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
+#if TD_FD_DEBUG
+#include <mutex>
+#include <set>
+#endif
+
+namespace td {
+
+int VERBOSITY_NAME(fd) = VERBOSITY_NAME(DEBUG) + 9;
+
+#if TD_FD_DEBUG
+class FdSet {
+ public:
+ void on_create_fd(NativeFd::Fd fd) {
+ if (!is_valid(fd)) {
+ return;
+ }
+ if (is_stdio(fd)) {
+ return;
+ }
+ std::unique_lock<std::mutex> guard(mutex_);
+ if (fds_.count(fd) >= 1) {
+ LOG(FATAL) << "Create duplicated fd: " << fd;
+ }
+ fds_.insert(fd);
+ }
+
+ Status validate(NativeFd::Fd fd) {
+ if (!is_valid(fd)) {
+ return Status::Error(PSLICE() << "Invalid fd: " << fd);
+ }
+ if (is_stdio(fd)) {
+ return Status::OK();
+ }
+ std::unique_lock<std::mutex> guard(mutex_);
+ if (fds_.count(fd) != 1) {
+ return Status::Error(PSLICE() << "Unknown fd: " << fd);
+ }
+ return Status::OK();
+ }
+
+ void on_close_fd(NativeFd::Fd fd) {
+ if (!is_valid(fd)) {
+ return;
+ }
+ if (is_stdio(fd)) {
+ return;
+ }
+ std::unique_lock<std::mutex> guard(mutex_);
+ if (fds_.count(fd) != 1) {
+ LOG(FATAL) << "Close unknown fd: " << fd;
+ }
+ fds_.erase(fd);
+ }
+
+ private:
+ std::mutex mutex_;
+ std::set<NativeFd::Fd> fds_;
+
+ bool is_stdio(NativeFd::Fd fd) const {
+#if TD_PORT_WINDOWS
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+ return fd == GetStdHandle(STD_INPUT_HANDLE) || fd == GetStdHandle(STD_OUTPUT_HANDLE) ||
+ fd == GetStdHandle(STD_ERROR_HANDLE);
+#else
+ return false;
+#endif
+#else
+ return fd >= 0 && fd <= 2;
+#endif
+ }
+
+ bool is_valid(NativeFd::Fd fd) const {
+#if TD_PORT_WINDOWS
+ return fd != INVALID_HANDLE_VALUE;
+#else
+ return fd >= 0;
+#endif
+ }
+};
+
+namespace {
+FdSet &get_fd_set() {
+ static FdSet res;
+ return res;
+}
+} // namespace
+
+#endif
+
+Status NativeFd::validate() const {
+#if TD_FD_DEBUG
+ return get_fd_set().validate(fd_);
+#else
+ return Status::OK();
+#endif
+}
+
+NativeFd::NativeFd(Fd fd) : fd_(fd) {
+ VLOG(fd) << *this << " create";
+#if TD_FD_DEBUG
+ get_fd_set().on_create_fd(fd_);
+#endif
+}
+
+NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) {
+#if TD_FD_DEBUG
+ get_fd_set().on_create_fd(fd_);
+#endif
+}
+
+#if TD_PORT_WINDOWS
+NativeFd::NativeFd(Socket socket) : fd_(reinterpret_cast<Fd>(socket)), is_socket_(true) {
+ VLOG(fd) << *this << " create";
+#if TD_FD_DEBUG
+ get_fd_set().on_create_fd(fd_);
+#endif
+}
+#endif
+
+NativeFd::NativeFd(NativeFd &&other) noexcept : fd_(other.fd_) {
+#if TD_PORT_WINDOWS
+ is_socket_ = other.is_socket_;
+#endif
+ other.fd_ = empty_fd();
+}
+
+NativeFd &NativeFd::operator=(NativeFd &&other) noexcept {
+ CHECK(this != &other);
+ close();
+ fd_ = other.fd_;
+#if TD_PORT_WINDOWS
+ is_socket_ = other.is_socket_;
+#endif
+ other.fd_ = empty_fd();
+ return *this;
+}
+
+NativeFd::~NativeFd() {
+ close();
+}
+
+NativeFd::operator bool() const noexcept {
+ return fd_ != empty_fd();
+}
+
+NativeFd::Fd NativeFd::empty_fd() {
+#if TD_PORT_POSIX
+ return -1;
+#elif TD_PORT_WINDOWS
+ return INVALID_HANDLE_VALUE;
+#endif
+}
+
+NativeFd::Fd NativeFd::fd() const {
+ return fd_;
+}
+
+NativeFd::Socket NativeFd::socket() const {
+#if TD_PORT_POSIX
+ return fd();
+#elif TD_PORT_WINDOWS
+ CHECK(is_socket_);
+ return reinterpret_cast<Socket>(fd_);
+#endif
+}
+
+Status NativeFd::set_is_blocking(bool is_blocking) const {
+#if TD_PORT_POSIX
+ auto old_flags = fcntl(fd(), F_GETFL);
+ if (old_flags == -1) {
+ return OS_SOCKET_ERROR("Failed to get socket flags");
+ }
+ auto new_flags = is_blocking ? old_flags & ~O_NONBLOCK : old_flags | O_NONBLOCK;
+ if (new_flags != old_flags && fcntl(fd(), F_SETFL, new_flags) == -1) {
+ return OS_SOCKET_ERROR("Failed to set socket flags");
+ }
+
+ return Status::OK();
+#elif TD_PORT_WINDOWS
+ return set_is_blocking_unsafe(is_blocking);
+#endif
+}
+
+Status NativeFd::set_is_blocking_unsafe(bool is_blocking) const {
+#if TD_PORT_POSIX
+ if (fcntl(fd(), F_SETFL, is_blocking ? 0 : O_NONBLOCK) == -1) {
+#elif TD_PORT_WINDOWS
+ u_long mode = is_blocking;
+ if (ioctlsocket(socket(), FIONBIO, &mode) != 0) {
+#endif
+ return OS_SOCKET_ERROR("Failed to change socket flags");
+ }
+ return Status::OK();
+}
+
+Status NativeFd::duplicate(const NativeFd &to) const {
+#if TD_PORT_POSIX
+ CHECK(*this);
+ CHECK(to);
+ if (dup2(fd(), to.fd()) == -1) {
+ return OS_ERROR("Failed to duplicate file descriptor");
+ }
+ return Status::OK();
+#elif TD_PORT_WINDOWS
+ return Status::Error("Not supported");
+#endif
+}
+
+void NativeFd::close() {
+ if (!*this) {
+ return;
+ }
+
+#if TD_FD_DEBUG
+ get_fd_set().on_close_fd(fd());
+#endif
+
+ VLOG(fd) << *this << " close";
+#if TD_PORT_WINDOWS
+ if (is_socket_ ? closesocket(socket()) : !CloseHandle(fd())) {
+#elif TD_PORT_POSIX
+ if (::close(fd()) < 0) {
+#endif
+ auto error = OS_ERROR("Close fd");
+ LOG(ERROR) << error;
+ }
+ fd_ = empty_fd();
+}
+
+NativeFd::Fd NativeFd::release() {
+ VLOG(fd) << *this << " release";
+ auto res = fd_;
+ fd_ = empty_fd();
+#if TD_FD_DEBUG
+ get_fd_set().on_close_fd(res);
+#endif
+ return res;
+}
+
+StringBuilder &operator<<(StringBuilder &sb, const NativeFd &fd) {
+ return sb << tag("fd", fd.fd());
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.h
new file mode 100644
index 0000000000..ac4bddccf2
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/NativeFd.h
@@ -0,0 +1,68 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/port/config.h"
+
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/Status.h"
+#include "td/utils/StringBuilder.h"
+
+namespace td {
+
+extern int VERBOSITY_NAME(fd);
+
+class NativeFd {
+ public:
+#if TD_PORT_POSIX
+ using Fd = int;
+ using Socket = int;
+#elif TD_PORT_WINDOWS
+ using Fd = HANDLE;
+ using Socket = SOCKET;
+#endif
+ NativeFd() = default;
+ explicit NativeFd(Fd fd);
+ NativeFd(Fd fd, bool nolog);
+#if TD_PORT_WINDOWS
+ explicit NativeFd(Socket socket);
+#endif
+ NativeFd(const NativeFd &) = delete;
+ NativeFd &operator=(const NativeFd &) = delete;
+ NativeFd(NativeFd &&other) noexcept;
+ NativeFd &operator=(NativeFd &&other) noexcept;
+ ~NativeFd();
+
+ explicit operator bool() const noexcept;
+
+ Fd fd() const;
+ Socket socket() const;
+
+ Status set_is_blocking(bool is_blocking) const;
+
+ Status set_is_blocking_unsafe(bool is_blocking) const; // may drop other Fd flags on non-Windows
+
+ Status duplicate(const NativeFd &to) const;
+
+ void close();
+ Fd release();
+
+ Status validate() const;
+
+ private:
+ static Fd empty_fd();
+
+ Fd fd_ = empty_fd();
+#if TD_PORT_WINDOWS
+ bool is_socket_{false};
+#endif
+};
+
+StringBuilder &operator<<(StringBuilder &sb, const NativeFd &fd);
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.cpp
index 87a7391802..c4431207f5 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -13,8 +13,11 @@ char disable_linker_warning_about_empty_file_poll_cpp TD_UNUSED;
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
+#include "td/utils/ScopeGuard.h"
#include "td/utils/Status.h"
+#include <cerrno>
+
namespace td {
namespace detail {
@@ -25,31 +28,37 @@ void Poll::clear() {
pollfds_.clear();
}
-void Poll::subscribe(const Fd &fd, Fd::Flags flags) {
- unsubscribe(fd);
+void Poll::subscribe(PollableFd fd, PollFlags flags) {
+ unsubscribe(fd.ref());
struct pollfd pollfd;
- pollfd.fd = fd.get_native_fd();
+ pollfd.fd = fd.native_fd().fd();
pollfd.events = 0;
- if (flags & Fd::Read) {
+ if (flags.can_read()) {
pollfd.events |= POLLIN;
}
- if (flags & Fd::Write) {
+ if (flags.can_write()) {
pollfd.events |= POLLOUT;
}
pollfd.revents = 0;
pollfds_.push_back(pollfd);
+ fds_.push_back(std::move(fd));
}
-void Poll::unsubscribe(const Fd &fd) {
+void Poll::unsubscribe(PollableFdRef fd_ref) {
+ auto fd = fd_ref.lock();
+ SCOPE_EXIT {
+ fd.release_as_list_node();
+ };
for (auto it = pollfds_.begin(); it != pollfds_.end(); ++it) {
- if (it->fd == fd.get_native_fd()) {
+ if (it->fd == fd.native_fd().fd()) {
pollfds_.erase(it);
+ fds_.erase(fds_.begin() + (it - pollfds_.begin()));
return;
}
}
}
-void Poll::unsubscribe_before_close(const Fd &fd) {
+void Poll::unsubscribe_before_close(PollableFdRef fd) {
unsubscribe(fd);
}
@@ -58,23 +67,26 @@ void Poll::run(int timeout_ms) {
auto poll_errno = errno;
LOG_IF(FATAL, err == -1 && poll_errno != EINTR) << Status::PosixError(poll_errno, "poll failed");
- for (auto &pollfd : pollfds_) {
- Fd::Flags flags = 0;
+ for (size_t i = 0; i < pollfds_.size(); i++) {
+ auto &pollfd = pollfds_[i];
+ auto &fd = fds_[i];
+
+ PollFlags flags;
if (pollfd.revents & POLLIN) {
pollfd.revents &= ~POLLIN;
- flags |= Fd::Read;
+ flags = flags | PollFlags::Read();
}
if (pollfd.revents & POLLOUT) {
pollfd.revents &= ~POLLOUT;
- flags |= Fd::Write;
+ flags = flags | PollFlags::Write();
}
if (pollfd.revents & POLLHUP) {
pollfd.revents &= ~POLLHUP;
- flags |= Fd::Close;
+ flags = flags | PollFlags::Close();
}
if (pollfd.revents & POLLERR) {
pollfd.revents &= ~POLLERR;
- flags |= Fd::Error;
+ flags = flags | PollFlags::Error();
}
if (pollfd.revents & POLLNVAL) {
LOG(FATAL) << "Unexpected POLLNVAL " << tag("fd", pollfd.fd);
@@ -82,7 +94,7 @@ void Poll::run(int timeout_ms) {
if (pollfd.revents) {
LOG(FATAL) << "Unsupported poll events: " << pollfd.revents;
}
- Fd(pollfd.fd, Fd::Mode::Reference).update_flags_notify(flags);
+ fd.add_flags(flags);
}
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.h
index 32eca75399..5f9f0e94f6 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Poll.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,8 +11,9 @@
#ifdef TD_POLL_POLL
#include "td/utils/common.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
+#include "td/utils/port/PollFlags.h"
#include <poll.h>
@@ -26,22 +27,27 @@ class Poll final : public PollBase {
Poll &operator=(const Poll &) = delete;
Poll(Poll &&) = delete;
Poll &operator=(Poll &&) = delete;
- ~Poll() override = default;
+ ~Poll() final = default;
- void init() override;
+ void init() final;
- void clear() override;
+ void clear() final;
- void subscribe(const Fd &fd, Fd::Flags flags) override;
+ void subscribe(PollableFd fd, PollFlags flags) final;
- void unsubscribe(const Fd &fd) override;
+ void unsubscribe(PollableFdRef fd) final;
- void unsubscribe_before_close(const Fd &fd) override;
+ void unsubscribe_before_close(PollableFdRef fd) final;
- void run(int timeout_ms) override;
+ void run(int timeout_ms) final;
+
+ static bool is_edge_triggered() {
+ return false;
+ }
private:
vector<pollfd> pollfds_;
+ vector<PollableFd> fds_;
};
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/PollableFd.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/PollableFd.h
new file mode 100644
index 0000000000..01fb5857e4
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/PollableFd.h
@@ -0,0 +1,230 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/format.h"
+#include "td/utils/List.h"
+#include "td/utils/logging.h"
+#include "td/utils/Observer.h"
+#include "td/utils/port/detail/NativeFd.h"
+#include "td/utils/port/Mutex.h"
+#include "td/utils/port/PollFlags.h"
+
+#include <atomic>
+#include <memory>
+
+namespace td {
+
+class PollableFdInfo;
+class PollableFdInfoUnlock {
+ public:
+ void operator()(PollableFdInfo *ptr);
+};
+
+class PollableFd;
+class PollableFdRef {
+ public:
+ explicit PollableFdRef(ListNode *list_node) : list_node_(list_node) {
+ }
+ PollableFd lock();
+
+ private:
+ ListNode *list_node_;
+};
+
+class PollableFd {
+ public:
+ // Interface for kqueue, epoll and e.t.c.
+ const NativeFd &native_fd() const;
+
+ ListNode *release_as_list_node();
+ PollableFdRef ref();
+ static PollableFd from_list_node(ListNode *node);
+ void add_flags(PollFlags flags);
+ PollFlags get_flags_unsafe() const;
+
+ private:
+ std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock> fd_info_;
+ friend class PollableFdInfo;
+
+ explicit PollableFd(std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock> fd_info) : fd_info_(std::move(fd_info)) {
+ }
+};
+
+inline PollableFd PollableFdRef::lock() {
+ return PollableFd::from_list_node(list_node_);
+}
+
+class PollableFdInfo final : private ListNode {
+ public:
+ PollableFdInfo() = default;
+ PollableFdInfo(const PollableFdInfo &) = delete;
+ PollableFdInfo &operator=(const PollableFdInfo &) = delete;
+ PollableFdInfo(PollableFdInfo &&) = delete;
+ PollableFdInfo &operator=(PollableFdInfo &&) = delete;
+
+ PollableFd extract_pollable_fd(ObserverBase *observer) {
+ VLOG(fd) << native_fd() << " extract pollable fd " << tag("observer", observer);
+ CHECK(!empty());
+ bool was_locked = lock_.test_and_set(std::memory_order_acquire);
+ CHECK(!was_locked);
+ set_observer(observer);
+ return PollableFd{std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock>{this}};
+ }
+ PollableFdRef get_pollable_fd_ref() {
+ CHECK(!empty());
+ bool was_locked = lock_.test_and_set(std::memory_order_acquire);
+ CHECK(was_locked);
+ return PollableFdRef{as_list_node()};
+ }
+
+ void add_flags(PollFlags flags) {
+ flags_.write_flags_local(flags);
+ }
+
+ void clear_flags(PollFlags flags) {
+ flags_.clear_flags(flags);
+ }
+ PollFlags sync_with_poll() const {
+ return flags_.read_flags();
+ }
+ PollFlags get_flags_local() const {
+ return flags_.read_flags_local();
+ }
+
+ bool empty() const {
+ return !fd_;
+ }
+
+ void set_native_fd(NativeFd new_native_fd) {
+ if (fd_) {
+ CHECK(!new_native_fd);
+ bool was_locked = lock_.test_and_set(std::memory_order_acquire);
+ CHECK(!was_locked);
+ lock_.clear(std::memory_order_release);
+ }
+
+ fd_ = std::move(new_native_fd);
+ }
+ explicit PollableFdInfo(NativeFd native_fd) {
+ set_native_fd(std::move(native_fd));
+ }
+ const NativeFd &native_fd() const {
+ //CHECK(!empty());
+ return fd_;
+ }
+ NativeFd move_as_native_fd() {
+ return std::move(fd_);
+ }
+
+ ~PollableFdInfo() {
+ VLOG(fd) << native_fd() << " destroy PollableFdInfo";
+ bool was_locked = lock_.test_and_set(std::memory_order_acquire);
+ CHECK(!was_locked);
+ }
+
+ void add_flags_from_poll(PollFlags flags) {
+ VLOG(fd) << native_fd() << " add flags from poll " << flags;
+ if (flags_.write_flags(flags)) {
+ notify_observer();
+ }
+ }
+
+ private:
+ NativeFd fd_{};
+ std::atomic_flag lock_ = ATOMIC_FLAG_INIT;
+ PollFlagsSet flags_;
+#if TD_PORT_WINDOWS
+ Mutex observer_lock_;
+#endif
+ ObserverBase *observer_{nullptr};
+
+ friend class PollableFd;
+ friend class PollableFdInfoUnlock;
+
+ void set_observer(ObserverBase *observer) {
+#if TD_PORT_WINDOWS
+ auto lock = observer_lock_.lock();
+#endif
+ CHECK(observer_ == nullptr);
+ observer_ = observer;
+ }
+ void clear_observer() {
+#if TD_PORT_WINDOWS
+ auto lock = observer_lock_.lock();
+#endif
+ observer_ = nullptr;
+ }
+ void notify_observer() {
+#if TD_PORT_WINDOWS
+ auto lock = observer_lock_.lock();
+#endif
+ VLOG(fd) << native_fd() << " notify " << tag("observer", observer_);
+ if (observer_ != nullptr) {
+ observer_->notify();
+ }
+ }
+
+ void unlock() {
+ clear_observer();
+ lock_.clear(std::memory_order_release);
+ as_list_node()->remove();
+ }
+
+ ListNode *as_list_node() {
+ return static_cast<ListNode *>(this);
+ }
+ static PollableFdInfo *from_list_node(ListNode *list_node) {
+ return static_cast<PollableFdInfo *>(list_node);
+ }
+};
+inline void PollableFdInfoUnlock::operator()(PollableFdInfo *ptr) {
+ ptr->unlock();
+}
+
+inline ListNode *PollableFd::release_as_list_node() {
+ return fd_info_.release()->as_list_node();
+}
+inline PollableFdRef PollableFd::ref() {
+ return PollableFdRef{fd_info_->as_list_node()};
+}
+inline PollableFd PollableFd::from_list_node(ListNode *node) {
+ return PollableFd(std::unique_ptr<PollableFdInfo, PollableFdInfoUnlock>(PollableFdInfo::from_list_node(node)));
+}
+
+inline void PollableFd::add_flags(PollFlags flags) {
+ fd_info_->add_flags_from_poll(flags);
+}
+inline PollFlags PollableFd::get_flags_unsafe() const {
+ return fd_info_->get_flags_local();
+}
+inline const NativeFd &PollableFd::native_fd() const {
+ return fd_info_->native_fd();
+}
+
+template <class FdT>
+void sync_with_poll(const FdT &fd) {
+ fd.get_poll_info().sync_with_poll();
+}
+
+template <class FdT>
+bool can_read_local(const FdT &fd) {
+ return fd.get_poll_info().get_flags_local().can_read() || fd.get_poll_info().get_flags_local().has_pending_error();
+}
+
+template <class FdT>
+bool can_write_local(const FdT &fd) {
+ return fd.get_poll_info().get_flags_local().can_write();
+}
+
+template <class FdT>
+bool can_close_local(const FdT &fd) {
+ return fd.get_poll_info().get_flags_local().can_close();
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.cpp
index b532a0464c..d149246fd1 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -29,22 +29,25 @@ void Select::clear() {
fds_.clear();
}
-void Select::subscribe(const Fd &fd, Fd::Flags flags) {
- int native_fd = fd.get_native_fd();
+void Select::subscribe(PollableFd fd, PollFlags flags) {
+ int native_fd = fd.native_fd().fd();
for (auto &it : fds_) {
- CHECK(it.fd_ref.get_native_fd() != native_fd);
+ CHECK(it.fd.native_fd().fd() != native_fd);
}
- fds_.push_back(FdInfo{Fd(native_fd, Fd::Mode::Reference), flags});
- CHECK(0 <= native_fd && native_fd < FD_SETSIZE) << native_fd << " " << FD_SETSIZE;
+ fds_.push_back(FdInfo{std::move(fd), flags});
+ LOG_CHECK(0 <= native_fd && native_fd < FD_SETSIZE) << native_fd << " " << FD_SETSIZE;
FD_SET(native_fd, &all_fd_);
if (native_fd > max_fd_) {
max_fd_ = native_fd;
}
}
-void Select::unsubscribe(const Fd &fd) {
- int native_fd = fd.get_native_fd();
- CHECK(0 <= native_fd && native_fd < FD_SETSIZE) << native_fd << " " << FD_SETSIZE;
+void Select::unsubscribe(PollableFdRef fd) {
+ auto fd_locked = fd.lock();
+ int native_fd = fd_locked.native_fd().fd();
+ fd_locked.release_as_list_node();
+
+ LOG_CHECK(0 <= native_fd && native_fd < FD_SETSIZE) << native_fd << " " << FD_SETSIZE;
FD_CLR(native_fd, &all_fd_);
FD_CLR(native_fd, &read_fd_);
FD_CLR(native_fd, &write_fd_);
@@ -53,17 +56,17 @@ void Select::unsubscribe(const Fd &fd) {
max_fd_--;
}
for (auto it = fds_.begin(); it != fds_.end();) {
- if (it->fd_ref.get_native_fd() == native_fd) {
+ if (it->fd.native_fd().fd() == native_fd) {
std::swap(*it, fds_.back());
fds_.pop_back();
break;
} else {
- it++;
+ ++it;
}
}
}
-void Select::unsubscribe_before_close(const Fd &fd) {
+void Select::unsubscribe_before_close(PollableFdRef fd) {
unsubscribe(fd);
}
@@ -79,14 +82,14 @@ void Select::run(int timeout_ms) {
}
for (auto &it : fds_) {
- int native_fd = it.fd_ref.get_native_fd();
- Fd::Flags fd_flags = it.fd_ref.get_flags();
- if ((it.flags & Fd::Write) && !(fd_flags & Fd::Write)) {
+ int native_fd = it.fd.native_fd().fd();
+ PollFlags fd_flags = it.fd.get_flags_unsafe(); // concurrent calls are UB
+ if (it.flags.can_write() && !fd_flags.can_write()) {
FD_SET(native_fd, &write_fd_);
} else {
FD_CLR(native_fd, &write_fd_);
}
- if ((it.flags & Fd::Read) && !(fd_flags & Fd::Read)) {
+ if (it.flags.can_read() && !fd_flags.can_read()) {
FD_SET(native_fd, &read_fd_);
} else {
FD_CLR(native_fd, &read_fd_);
@@ -96,20 +99,18 @@ void Select::run(int timeout_ms) {
select(max_fd_ + 1, &read_fd_, &write_fd_, &except_fd_, timeout_ptr);
for (auto &it : fds_) {
- int native_fd = it.fd_ref.get_native_fd();
- Fd::Flags flags = 0;
+ int native_fd = it.fd.native_fd().fd();
+ PollFlags flags;
if (FD_ISSET(native_fd, &read_fd_)) {
- flags |= Fd::Read;
+ flags = flags | PollFlags::Read();
}
if (FD_ISSET(native_fd, &write_fd_)) {
- flags |= Fd::Write;
+ flags = flags | PollFlags::Write();
}
if (FD_ISSET(native_fd, &except_fd_)) {
- flags |= Fd::Error;
- }
- if (flags != 0) {
- it.fd_ref.update_flags_notify(flags);
+ flags = flags | PollFlags::Error();
}
+ it.fd.add_flags(flags);
}
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.h
index 17f2876f3a..cc8d6a3bc0 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/Select.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,8 +11,9 @@
#ifdef TD_POLL_SELECT
#include "td/utils/common.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
+#include "td/utils/port/PollFlags.h"
#include <sys/select.h>
@@ -26,24 +27,28 @@ class Select final : public PollBase {
Select &operator=(const Select &) = delete;
Select(Select &&) = delete;
Select &operator=(Select &&) = delete;
- ~Select() override = default;
+ ~Select() final = default;
- void init() override;
+ void init() final;
- void clear() override;
+ void clear() final;
- void subscribe(const Fd &fd, Fd::Flags flags) override;
+ void subscribe(PollableFd fd, PollFlags flags) final;
- void unsubscribe(const Fd &fd) override;
+ void unsubscribe(PollableFdRef fd) final;
- void unsubscribe_before_close(const Fd &fd) override;
+ void unsubscribe_before_close(PollableFdRef fd) final;
- void run(int timeout_ms) override;
+ void run(int timeout_ms) final;
+
+ static bool is_edge_triggered() {
+ return false;
+ }
private:
struct FdInfo {
- Fd fd_ref;
- Fd::Flags flags;
+ PollableFd fd;
+ PollFlags flags;
};
vector<FdInfo> fds_;
fd_set all_fd_;
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.cpp
index d949945e1d..4b6366980e 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.cpp
@@ -1,16 +1,17 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/port/detail/ThreadIdGuard.h"
-#include "td/utils/logging.h"
+#include "td/utils/common.h"
+#include "td/utils/ExitGuard.h"
#include "td/utils/port/thread_local.h"
-#include <array>
#include <mutex>
+#include <set>
namespace td {
namespace detail {
@@ -18,34 +19,37 @@ class ThreadIdManager {
public:
int32 register_thread() {
std::lock_guard<std::mutex> guard(mutex_);
- for (size_t i = 0; i < is_id_used_.size(); i++) {
- if (!is_id_used_[i]) {
- is_id_used_[i] = true;
- return static_cast<int32>(i + 1);
- }
+ if (unused_thread_ids_.empty()) {
+ return ++max_thread_id_;
}
- LOG(FATAL) << "Cannot create more than " << max_thread_count() << " threads";
- return 0;
+ auto it = unused_thread_ids_.begin();
+ auto result = *it;
+ unused_thread_ids_.erase(it);
+ return result;
}
void unregister_thread(int32 thread_id) {
- thread_id--;
std::lock_guard<std::mutex> guard(mutex_);
- CHECK(is_id_used_.at(thread_id));
- is_id_used_[thread_id] = false;
+ CHECK(0 < thread_id && thread_id <= max_thread_id_);
+ bool is_inserted = unused_thread_ids_.insert(thread_id).second;
+ CHECK(is_inserted);
}
private:
std::mutex mutex_;
- std::array<bool, max_thread_count()> is_id_used_{{false}};
+ std::set<int32> unused_thread_ids_;
+ int32 max_thread_id_ = 0;
};
static ThreadIdManager thread_id_manager;
+static ExitGuard exit_guard;
ThreadIdGuard::ThreadIdGuard() {
thread_id_ = thread_id_manager.register_thread();
set_thread_id(thread_id_);
}
ThreadIdGuard::~ThreadIdGuard() {
- thread_id_manager.unregister_thread(thread_id_);
+ if (!ExitGuard::is_exited()) {
+ thread_id_manager.unregister_thread(thread_id_);
+ }
set_thread_id(0);
}
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.h
index 434bd5ac4d..b05d9a62a2 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadIdGuard.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.cpp
new file mode 100644
index 0000000000..3fa0d19389
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.cpp
@@ -0,0 +1,217 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/detail/ThreadPthread.h"
+
+char disable_linker_warning_about_empty_file_thread_pthread_cpp TD_UNUSED;
+
+#if TD_THREAD_PTHREAD
+
+#include "td/utils/misc.h"
+#include "td/utils/port/detail/skip_eintr.h"
+#if TD_NETBSD
+#include "td/utils/ScopeGuard.h"
+#endif
+
+#include <pthread.h>
+#if TD_FREEBSD
+#include <pthread_np.h>
+#endif
+#include <sched.h>
+#include <signal.h>
+#if TD_FREEBSD
+#include <sys/cpuset.h>
+#endif
+#if TD_FREEBSD || TD_OPENBSD || TD_NETBSD
+#include <sys/sysctl.h>
+#endif
+#include <unistd.h>
+
+namespace td {
+namespace detail {
+unsigned ThreadPthread::hardware_concurrency() {
+// Linux and macOS
+#if defined(_SC_NPROCESSORS_ONLN)
+ {
+ auto res = sysconf(_SC_NPROCESSORS_ONLN);
+ if (res > 0) {
+ return narrow_cast<unsigned>(res);
+ }
+ }
+#endif
+
+#if TD_FREEBSD || TD_OPENBSD || TD_NETBSD
+#if defined(HW_AVAILCPU) && defined(CTL_HW)
+ {
+ int mib[2] = {CTL_HW, HW_AVAILCPU};
+ int res{0};
+ size_t len = sizeof(res);
+ if (sysctl(mib, 2, &res, &len, nullptr, 0) == 0 && res != 0) {
+ return res;
+ }
+ }
+#endif
+
+#if defined(HW_NCPU) && defined(CTL_HW)
+ {
+ int mib[2] = {CTL_HW, HW_NCPU};
+ int res{0};
+ size_t len = sizeof(res);
+ if (sysctl(mib, 2, &res, &len, nullptr, 0) == 0 && res != 0) {
+ return res;
+ }
+ }
+#endif
+#endif
+
+ // Just in case
+ return 8;
+}
+
+void ThreadPthread::set_name(CSlice name) {
+#if defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
+#if __GLIBC_PREREQ(2, 12)
+ pthread_setname_np(thread_, name.c_str());
+#endif
+#endif
+}
+
+void ThreadPthread::join() {
+ if (is_inited_.get()) {
+ is_inited_ = false;
+ pthread_join(thread_, nullptr);
+ }
+}
+
+void ThreadPthread::detach() {
+ if (is_inited_.get()) {
+ is_inited_ = false;
+ pthread_detach(thread_);
+ }
+}
+
+void ThreadPthread::send_real_time_signal(id thread_id, int real_time_signal_number) {
+#ifdef SIGRTMIN
+ pthread_kill(thread_id, SIGRTMIN + real_time_signal_number);
+#endif
+}
+
+int ThreadPthread::do_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *arg) {
+ return pthread_create(thread, attr, start_routine, arg);
+}
+
+#if TD_HAVE_THREAD_AFFINITY
+Status ThreadPthread::set_affinity_mask(id thread_id, uint64 mask) {
+#if TD_LINUX || TD_FREEBSD
+#if TD_FREEBSD
+ cpuset_t cpuset;
+#else
+ cpu_set_t cpuset;
+#endif
+ CPU_ZERO(&cpuset);
+ for (int j = 0; j < 64 && j < CPU_SETSIZE; j++) {
+ if ((mask >> j) & 1) {
+ CPU_SET(j, &cpuset);
+ }
+ }
+
+ auto res = skip_eintr([&] { return pthread_setaffinity_np(thread_id, sizeof(cpuset), &cpuset); });
+ if (res) {
+ return OS_ERROR("Failed to set thread affinity mask");
+ }
+ return Status::OK();
+#elif TD_NETBSD
+ cpuset_t *cpuset = cpuset_create();
+ if (cpuset == nullptr) {
+ return OS_ERROR("Failed to create cpuset");
+ }
+ SCOPE_EXIT {
+ cpuset_destroy(cpuset);
+ };
+ for (int j = 0; j < 64; j++) {
+ if ((mask >> j) & 1) {
+ if (cpuset_set(j, cpuset) != 0) {
+ return OS_ERROR("Failed to set CPU identifier");
+ }
+ }
+ }
+
+ auto res = skip_eintr([&] { return pthread_setaffinity_np(thread_id, cpuset_size(cpuset), cpuset); });
+ if (res) {
+ return OS_ERROR("Failed to set thread affinity mask");
+ }
+ if (get_affinity_mask(thread_id) != mask) {
+ return Status::Error("Failed to set exact thread affinity mask");
+ }
+ return Status::OK();
+#else
+ return Status::Error("Unsupported");
+#endif
+}
+
+uint64 ThreadPthread::get_affinity_mask(id thread_id) {
+#if TD_LINUX || TD_FREEBSD
+#if TD_FREEBSD
+ cpuset_t cpuset;
+#else
+ cpu_set_t cpuset;
+#endif
+ CPU_ZERO(&cpuset);
+ auto res = skip_eintr([&] { return pthread_getaffinity_np(thread_id, sizeof(cpuset), &cpuset); });
+ if (res) {
+ return 0;
+ }
+
+ uint64 mask = 0;
+ for (int j = 0; j < 64 && j < CPU_SETSIZE; j++) {
+ if (CPU_ISSET(j, &cpuset)) {
+ mask |= static_cast<uint64>(1) << j;
+ }
+ }
+ return mask;
+#elif TD_NETBSD
+ cpuset_t *cpuset = cpuset_create();
+ if (cpuset == nullptr) {
+ return 0;
+ }
+ SCOPE_EXIT {
+ cpuset_destroy(cpuset);
+ };
+ auto res = skip_eintr([&] { return pthread_getaffinity_np(thread_id, cpuset_size(cpuset), cpuset); });
+ if (res) {
+ return 0;
+ }
+
+ uint64 mask = 0;
+ for (int j = 0; j < 64; j++) {
+ if (cpuset_isset(j, cpuset) > 0) {
+ mask |= static_cast<uint64>(1) << j;
+ }
+ }
+ if (mask == 0) {
+ // the mask wasn't set, all CPUs are allowed
+ auto proc_count = sysconf(_SC_NPROCESSORS_ONLN);
+ for (int j = 0; j < 64 && j < proc_count; j++) {
+ mask |= static_cast<uint64>(1) << j;
+ }
+ }
+ return mask;
+#else
+ return 0;
+#endif
+}
+#endif
+
+namespace this_thread_pthread {
+ThreadPthread::id get_id() {
+ return pthread_self();
+}
+} // namespace this_thread_pthread
+
+} // namespace detail
+} // namespace td
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.h
index e42efc3771..b938b58c7e 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadPthread.h
@@ -1,89 +1,112 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+
#include "td/utils/port/config.h"
#ifdef TD_THREAD_PTHREAD
#include "td/utils/common.h"
+#include "td/utils/Destructor.h"
#include "td/utils/invoke.h"
#include "td/utils/MovableValue.h"
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/port/thread_local.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
#include <tuple>
#include <type_traits>
#include <utility>
+#if TD_OPENBSD || TD_SOLARIS
#include <pthread.h>
-#include <sched.h>
+#endif
+#include <sys/types.h>
+
+#if TD_LINUX || TD_FREEBSD || TD_NETBSD
+#define TD_HAVE_THREAD_AFFINITY 1
+#endif
namespace td {
namespace detail {
+
class ThreadPthread {
public:
ThreadPthread() = default;
ThreadPthread(const ThreadPthread &other) = delete;
ThreadPthread &operator=(const ThreadPthread &other) = delete;
- ThreadPthread(ThreadPthread &&) = default;
- ThreadPthread &operator=(ThreadPthread &&) = default;
+ ThreadPthread(ThreadPthread &&other) noexcept : is_inited_(std::move(other.is_inited_)), thread_(other.thread_) {
+ }
+ ThreadPthread &operator=(ThreadPthread &&other) noexcept {
+ join();
+ is_inited_ = std::move(other.is_inited_);
+ thread_ = other.thread_;
+ return *this;
+ }
template <class Function, class... Args>
- explicit ThreadPthread(Function &&f, Args &&... args) {
- func_ = std::make_unique<std::unique_ptr<Destructor>>(
- create_destructor([args = std::make_tuple(decay_copy(std::forward<Function>(f)),
- decay_copy(std::forward<Args>(args))...)]() mutable {
- invoke_tuple(std::move(args));
- clear_thread_locals();
- }));
- pthread_create(&thread_, nullptr, run_thread, func_.get());
+ explicit ThreadPthread(Function &&f, Args &&...args) {
+ auto func = create_destructor([args = std::make_tuple(decay_copy(std::forward<Function>(f)),
+ decay_copy(std::forward<Args>(args))...)]() mutable {
+ invoke_tuple(std::move(args));
+ clear_thread_locals();
+ });
+ do_pthread_create(&thread_, nullptr, run_thread, func.release());
is_inited_ = true;
}
- void join() {
- if (is_inited_.get()) {
- is_inited_ = false;
- pthread_join(thread_, nullptr);
- }
- }
~ThreadPthread() {
join();
}
- static unsigned hardware_concurrency() {
- return 8;
- }
+ void set_name(CSlice name);
+
+ void join();
+
+ void detach();
+
+ static unsigned hardware_concurrency();
using id = pthread_t;
+ id get_id() noexcept {
+ return thread_;
+ }
+
+ static void send_real_time_signal(id thread_id, int real_time_signal_number);
+
+#if TD_HAVE_THREAD_AFFINITY
+ static Status set_affinity_mask(id thread_id, uint64 mask);
+
+ static uint64 get_affinity_mask(id thread_id);
+#endif
+
private:
MovableValue<bool> is_inited_;
pthread_t thread_;
- std::unique_ptr<std::unique_ptr<Destructor>> func_;
template <class T>
std::decay_t<T> decay_copy(T &&v) {
return std::forward<T>(v);
}
+ static int do_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),
+ void *arg);
+
static void *run_thread(void *ptr) {
ThreadIdGuard thread_id_guard;
- auto func = static_cast<decltype(func_.get())>(ptr);
- func->reset();
+ auto func = unique_ptr<Destructor>(static_cast<Destructor *>(ptr));
return nullptr;
}
};
namespace this_thread_pthread {
-inline void yield() {
- sched_yield();
-}
-inline ThreadPthread::id get_id() {
- return pthread_self();
-}
+ThreadPthread::id get_id();
} // namespace this_thread_pthread
+
} // namespace detail
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadStl.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadStl.h
index 64bf3213cf..06dec7ccc0 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadStl.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/ThreadStl.h
@@ -1,26 +1,37 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+
#include "td/utils/port/config.h"
#ifdef TD_THREAD_STL
#include "td/utils/common.h"
#include "td/utils/invoke.h"
+#if TD_WINDOWS && TD_MSVC
+#include "td/utils/port/detail/NativeFd.h"
+#endif
#include "td/utils/port/detail/ThreadIdGuard.h"
#include "td/utils/port/thread_local.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
#include <thread>
#include <tuple>
#include <type_traits>
#include <utility>
+#if TD_WINDOWS && TD_MSVC
+#define TD_HAVE_THREAD_AFFINITY 1
+#endif
+
namespace td {
namespace detail {
+
class ThreadStl {
public:
ThreadStl() = default;
@@ -28,9 +39,12 @@ class ThreadStl {
ThreadStl &operator=(const ThreadStl &other) = delete;
ThreadStl(ThreadStl &&) = default;
ThreadStl &operator=(ThreadStl &&) = default;
- ~ThreadStl() = default;
+ ~ThreadStl() {
+ join();
+ }
+
template <class Function, class... Args>
- explicit ThreadStl(Function &&f, Args &&... args) {
+ explicit ThreadStl(Function &&f, Args &&...args) {
thread_ = std::thread([args = std::make_tuple(decay_copy(std::forward<Function>(f)),
decay_copy(std::forward<Args>(args))...)]() mutable {
ThreadIdGuard thread_id_guard;
@@ -40,14 +54,79 @@ class ThreadStl {
}
void join() {
- thread_.join();
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+ }
+
+ void detach() {
+ if (thread_.joinable()) {
+ thread_.detach();
+ }
+ }
+
+ void set_name(CSlice name) {
+ // not supported
}
static unsigned hardware_concurrency() {
return std::thread::hardware_concurrency();
}
+#if TD_WINDOWS && TD_MSVC
+ using id = DWORD;
+#else
using id = std::thread::id;
+#endif
+
+ id get_id() noexcept {
+#if TD_WINDOWS && TD_MSVC
+ static_assert(std::is_same<decltype(thread_.native_handle()), HANDLE>::value,
+ "Expected HANDLE as native thread type");
+ return GetThreadId(thread_.native_handle());
+#else
+ return thread_.get_id();
+#endif
+ }
+
+ static void send_real_time_signal(id thread_id, int real_time_signal_number) {
+ // not supported
+ }
+
+#if TD_HAVE_THREAD_AFFINITY
+ static Status set_affinity_mask(id thread_id, uint64 mask) {
+ if (static_cast<DWORD_PTR>(mask) != mask) {
+ return Status::Error("Invalid thread affinity mask specified");
+ }
+ auto handle = OpenThread(THREAD_SET_LIMITED_INFORMATION | THREAD_QUERY_LIMITED_INFORMATION, FALSE, thread_id);
+ if (handle == nullptr) {
+ return Status::Error("Failed to access thread");
+ }
+ NativeFd thread_handle(handle);
+ if (SetThreadAffinityMask(thread_handle.fd(), static_cast<DWORD_PTR>(mask))) {
+ return Status::OK();
+ }
+ return OS_ERROR("Failed to set thread affinity mask");
+ }
+
+ static uint64 get_affinity_mask(id thread_id) {
+ DWORD_PTR process_mask = 0;
+ DWORD_PTR system_mask = 0;
+ if (GetProcessAffinityMask(GetCurrentProcess(), &process_mask, &system_mask)) {
+ auto handle = OpenThread(THREAD_SET_LIMITED_INFORMATION | THREAD_QUERY_LIMITED_INFORMATION, FALSE, thread_id);
+ if (handle == nullptr) {
+ return 0;
+ }
+ NativeFd thread_handle(handle);
+ auto result = SetThreadAffinityMask(thread_handle.fd(), process_mask);
+ if (result != 0 && result != process_mask) {
+ SetThreadAffinityMask(thread_handle.fd(), result);
+ }
+ return result;
+ }
+ return 0;
+ }
+#endif
private:
std::thread thread_;
@@ -57,7 +136,17 @@ class ThreadStl {
return std::forward<T>(v);
}
};
-namespace this_thread_stl = std::this_thread;
+
+namespace this_thread_stl {
+#if TD_WINDOWS && TD_MSVC
+inline ThreadStl::id get_id() {
+ return GetCurrentThreadId();
+}
+#else
+using std::this_thread::get_id;
+#endif
+} // namespace this_thread_stl
+
} // namespace detail
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.cpp
index 8f443d29ab..6f40ee4a13 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,84 +11,30 @@ char disable_linker_warning_about_empty_file_wineventpoll_cpp TD_UNUSED;
#ifdef TD_POLL_WINEVENT
#include "td/utils/common.h"
-#include "td/utils/logging.h"
-#include "td/utils/misc.h"
-#include "td/utils/port/Fd.h"
-#include "td/utils/port/PollBase.h"
-#include "td/utils/port/sleep.h"
-#include "td/utils/Status.h"
-
-#include <utility>
namespace td {
namespace detail {
void WineventPoll::init() {
- clear();
}
void WineventPoll::clear() {
- fds_.clear();
}
-void WineventPoll::subscribe(const Fd &fd, Fd::Flags flags) {
- for (auto &it : fds_) {
- if (it.fd_ref.get_key() == fd.get_key()) {
- it.flags = flags;
- return;
- }
- }
- fds_.push_back({fd.clone(), flags});
+void WineventPoll::subscribe(PollableFd fd, PollFlags flags) {
+ fd.release_as_list_node();
}
-void WineventPoll::unsubscribe(const Fd &fd) {
- for (auto it = fds_.begin(); it != fds_.end(); ++it) {
- if (it->fd_ref.get_key() == fd.get_key()) {
- std::swap(*it, fds_.back());
- fds_.pop_back();
- return;
- }
- }
+void WineventPoll::unsubscribe(PollableFdRef fd) {
+ auto pollable_fd = fd.lock(); // unlocked in destructor
}
-void WineventPoll::unsubscribe_before_close(const Fd &fd) {
- unsubscribe(fd);
+void WineventPoll::unsubscribe_before_close(PollableFdRef fd) {
+ unsubscribe(std::move(fd));
}
void WineventPoll::run(int timeout_ms) {
- vector<std::pair<size_t, Fd::Flag>> events_desc;
- vector<HANDLE> events;
- for (size_t i = 0; i < fds_.size(); i++) {
- auto &fd_info = fds_[i];
- if (fd_info.flags & Fd::Flag::Write) {
- events_desc.emplace_back(i, Fd::Flag::Write);
- events.push_back(fd_info.fd_ref.get_write_event());
- }
- if (fd_info.flags & Fd::Flag::Read) {
- events_desc.emplace_back(i, Fd::Flag::Read);
- events.push_back(fd_info.fd_ref.get_read_event());
- }
- }
- if (events.empty()) {
- usleep_for(timeout_ms * 1000);
- return;
- }
-
- auto status = WaitForMultipleObjects(narrow_cast<DWORD>(events.size()), events.data(), false, timeout_ms);
- if (status == WAIT_FAILED) {
- auto error = OS_ERROR("WaitForMultipleObjects failed");
- LOG(FATAL) << events.size() << " " << timeout_ms << " " << error;
- }
- for (size_t i = 0; i < events.size(); i++) {
- if (WaitForSingleObject(events[i], 0) == WAIT_OBJECT_0) {
- auto &fd = fds_[events_desc[i].first].fd_ref;
- if (events_desc[i].second == Fd::Flag::Read) {
- fd.on_read_event();
- } else {
- fd.on_write_event();
- }
- }
- }
+ UNREACHABLE();
}
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.h
index ecc93f33fa..f68bbac48c 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/WineventPoll.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -11,8 +11,9 @@
#ifdef TD_POLL_WINEVENT
#include "td/utils/common.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/PollBase.h"
+#include "td/utils/port/PollFlags.h"
namespace td {
namespace detail {
@@ -24,26 +25,23 @@ class WineventPoll final : public PollBase {
WineventPoll &operator=(const WineventPoll &) = delete;
WineventPoll(WineventPoll &&) = delete;
WineventPoll &operator=(WineventPoll &&) = delete;
- ~WineventPoll() override = default;
+ ~WineventPoll() final = default;
- void init() override;
+ void init() final;
- void clear() override;
+ void clear() final;
- void subscribe(const Fd &fd, Fd::Flags flags) override;
+ void subscribe(PollableFd fd, PollFlags flags) final;
- void unsubscribe(const Fd &fd) override;
+ void unsubscribe(PollableFdRef fd) final;
- void unsubscribe_before_close(const Fd &fd) override;
+ void unsubscribe_before_close(PollableFdRef fd) final;
- void run(int timeout_ms) override;
+ void run(int timeout_ms) final;
- private:
- struct FdInfo {
- Fd fd_ref;
- Fd::Flags flags;
- };
- vector<FdInfo> fds_;
+ static bool is_edge_triggered() {
+ return true;
+ }
};
} // namespace detail
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/skip_eintr.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/skip_eintr.h
new file mode 100644
index 0000000000..6fde635680
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/detail/skip_eintr.h
@@ -0,0 +1,63 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#if TD_PORT_POSIX
+#include "td/utils/common.h"
+#include "td/utils/Time.h"
+
+#include <cerrno>
+#include <type_traits>
+#endif
+
+namespace td {
+
+#if TD_PORT_POSIX
+namespace detail {
+template <class F>
+auto skip_eintr(F &&f) {
+ decltype(f()) res;
+ static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
+ do {
+ errno = 0; // just in case
+ res = f();
+ } while (res < 0 && errno == EINTR);
+ return res;
+}
+
+template <class F>
+auto skip_eintr_cstr(F &&f) {
+ char *res;
+ do {
+ errno = 0; // just in case
+ res = f();
+ } while (res == nullptr && errno == EINTR);
+ return res;
+}
+
+template <class F>
+auto skip_eintr_timeout(F &&f, int32 timeout_ms) {
+ decltype(f(timeout_ms)) res;
+ static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
+
+ auto start = Timestamp::now();
+ auto left_timeout_ms = timeout_ms;
+ while (true) {
+ errno = 0; // just in case
+ res = f(left_timeout_ms);
+ if (res >= 0 || errno != EINTR) {
+ break;
+ }
+ left_timeout_ms =
+ static_cast<int32>(td::max((start.at() - Timestamp::now().at()) * 1000 + timeout_ms + 1 - 1e-9, 0.0));
+ }
+ return res;
+}
+} // namespace detail
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.cpp
index 8b169fefa4..2f77457ac4 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.cpp
@@ -1,19 +1,28 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/port/path.h"
-#include "td/utils/port/Fd.h"
+#include "td/utils/port/config.h"
-#if TD_WINDOWS
+#include "td/utils/format.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/detail/skip_eintr.h"
+#include "td/utils/ScopeGuard.h"
+#include "td/utils/SliceBuilder.h"
+
+#if TD_PORT_WINDOWS
+#include "td/utils/port/FromApp.h"
+#include "td/utils/port/wstring_convert.h"
#include "td/utils/Random.h"
#endif
#if TD_PORT_POSIX
+#include <dirent.h>
#include <limits.h>
#include <stdio.h>
@@ -32,7 +41,13 @@
#endif
+#if TD_DARWIN
+#include <sys/syslimits.h>
+#endif
+
+#include <cerrno>
#include <cstdlib>
+#include <string>
namespace td {
@@ -44,8 +59,7 @@ Status set_temporary_dir(CSlice dir) {
input_dir += TD_DIR_SLASH;
}
TRY_STATUS(mkpath(input_dir, 0750));
- TRY_RESULT(real_dir, realpath(input_dir));
- temporary_dir = std::move(real_dir);
+ TRY_RESULT_ASSIGN(temporary_dir, realpath(input_dir));
return Status::OK();
}
@@ -54,22 +68,47 @@ Status mkpath(CSlice path, int32 mode) {
Status last_error = Status::OK();
for (size_t i = 1; i < path.size(); i++) {
if (path[i] == TD_DIR_SLASH) {
- last_error = mkdir(path.substr(0, i).str(), mode);
+ last_error = mkdir(PSLICE() << path.substr(0, i), mode);
if (last_error.is_error() && first_error.is_ok()) {
first_error = last_error.clone();
}
}
}
if (last_error.is_error()) {
- return first_error;
+ if (last_error.message() == first_error.message() && last_error.code() == first_error.code()) {
+ return first_error;
+ }
+ return last_error.move_as_error_suffix(PSLICE() << ": " << first_error);
}
return Status::OK();
}
+Status rmrf(CSlice path) {
+ return walk_path(path, [](CSlice path, WalkPath::Type type) {
+ switch (type) {
+ case WalkPath::Type::EnterDir:
+ break;
+ case WalkPath::Type::ExitDir:
+ rmdir(path).ignore();
+ break;
+ case WalkPath::Type::NotDir:
+ unlink(path).ignore();
+ break;
+ }
+ });
+}
+
#if TD_PORT_POSIX
Status mkdir(CSlice dir, int32 mode) {
- int mkdir_res = skip_eintr([&] { return ::mkdir(dir.c_str(), static_cast<mode_t>(mode)); });
+ int mkdir_res = [&] {
+ int res;
+ do {
+ errno = 0; // just in case
+ res = ::mkdir(dir.c_str(), static_cast<mode_t>(mode));
+ } while (res < 0 && (errno == EINTR || errno == EAGAIN));
+ return res;
+ }();
if (mkdir_res == 0) {
return Status::OK();
}
@@ -82,7 +121,7 @@ Status mkdir(CSlice dir, int32 mode) {
}
Status rename(CSlice from, CSlice to) {
- int rename_res = skip_eintr([&] { return ::rename(from.c_str(), to.c_str()); });
+ int rename_res = detail::skip_eintr([&] { return ::rename(from.c_str(), to.c_str()); });
if (rename_res < 0) {
return OS_ERROR(PSLICE() << "Can't rename \"" << from << "\" to \"" << to << '\"');
}
@@ -92,9 +131,9 @@ Status rename(CSlice from, CSlice to) {
Result<string> realpath(CSlice slice, bool ignore_access_denied) {
char full_path[PATH_MAX + 1];
string res;
- char *err = skip_eintr_cstr([&] { return ::realpath(slice.c_str(), full_path); });
+ char *err = detail::skip_eintr_cstr([&] { return ::realpath(slice.c_str(), full_path); });
if (err != full_path) {
- if (ignore_access_denied && errno == EACCES) {
+ if (ignore_access_denied && (errno == EACCES || errno == EPERM)) {
res = slice.str();
} else {
return OS_ERROR(PSLICE() << "Realpath failed for \"" << slice << '"');
@@ -114,7 +153,7 @@ Result<string> realpath(CSlice slice, bool ignore_access_denied) {
}
Status chdir(CSlice dir) {
- int chdir_res = skip_eintr([&] { return ::chdir(dir.c_str()); });
+ int chdir_res = detail::skip_eintr([&] { return ::chdir(dir.c_str()); });
if (chdir_res) {
return OS_ERROR(PSLICE() << "Can't change directory to \"" << dir << '"');
}
@@ -122,7 +161,7 @@ Status chdir(CSlice dir) {
}
Status rmdir(CSlice dir) {
- int rmdir_res = skip_eintr([&] { return ::rmdir(dir.c_str()); });
+ int rmdir_res = detail::skip_eintr([&] { return ::rmdir(dir.c_str()); });
if (rmdir_res) {
return OS_ERROR(PSLICE() << "Can't delete directory \"" << dir << '"');
}
@@ -130,7 +169,7 @@ Status rmdir(CSlice dir) {
}
Status unlink(CSlice path) {
- int unlink_res = skip_eintr([&] { return ::unlink(path.c_str()); });
+ int unlink_res = detail::skip_eintr([&] { return ::unlink(path.c_str()); });
if (unlink_res) {
return OS_ERROR(PSLICE() << "Can't unlink \"" << path << '"');
}
@@ -177,7 +216,7 @@ Result<std::pair<FileFd, string>> mkstemp(CSlice dir) {
}
file_pattern += "tmpXXXXXXXXXX";
- int fd = skip_eintr([&] { return ::mkstemp(&file_pattern[0]); });
+ int fd = detail::skip_eintr([&] { return ::mkstemp(&file_pattern[0]); });
if (fd == -1) {
return OS_ERROR(PSLICE() << "Can't create temporary file \"" << file_pattern << '"');
}
@@ -209,20 +248,159 @@ Result<string> mkdtemp(CSlice dir, Slice prefix) {
dir_pattern.append(prefix.begin(), prefix.size());
dir_pattern += "XXXXXX";
- char *result = skip_eintr_cstr([&] { return ::mkdtemp(&dir_pattern[0]); });
+ char *result = detail::skip_eintr_cstr([&] { return ::mkdtemp(&dir_pattern[0]); });
if (result == nullptr) {
return OS_ERROR(PSLICE() << "Can't create temporary directory \"" << dir_pattern << '"');
}
return result;
}
+namespace detail {
+using WalkFunction = std::function<WalkPath::Action(CSlice name, WalkPath::Type type)>;
+Result<bool> walk_path_dir(string &path, FileFd fd, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
+
+Result<bool> walk_path_dir(string &path, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
+
+Result<bool> walk_path_file(string &path, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
+
+Result<bool> walk_path(string &path, const WalkFunction &func) TD_WARN_UNUSED_RESULT;
+
+Result<bool> walk_path_subdir(string &path, DIR *dir, const WalkFunction &func) {
+ while (true) {
+ errno = 0;
+ auto *entry = readdir(dir);
+ auto readdir_errno = errno;
+ if (readdir_errno) {
+ return Status::PosixError(readdir_errno, "readdir");
+ }
+ if (entry == nullptr) {
+ return true;
+ }
+ Slice name = Slice(static_cast<const char *>(entry->d_name));
+ if (name == "." || name == "..") {
+ continue;
+ }
+ auto size = path.size();
+ if (path.back() != TD_DIR_SLASH) {
+ path += TD_DIR_SLASH;
+ }
+ path.append(name.begin(), name.size());
+ SCOPE_EXIT {
+ path.resize(size);
+ };
+ Result<bool> status = true;
+#ifdef DT_DIR
+ if (entry->d_type == DT_UNKNOWN) {
+ status = walk_path(path, func);
+ } else if (entry->d_type == DT_DIR) {
+ status = walk_path_dir(path, func);
+ } else if (entry->d_type == DT_REG) {
+ status = walk_path_file(path, func);
+ }
+#else
+#if !TD_SOLARIS
+#warning "Slow walk_path"
+#endif
+ status = walk_path(path, func);
+#endif
+ if (status.is_error() || !status.ok()) {
+ return status;
+ }
+ }
+}
+
+Result<bool> walk_path_dir(string &path, DIR *subdir, const WalkFunction &func) {
+ SCOPE_EXIT {
+ closedir(subdir);
+ };
+ switch (func(path, WalkPath::Type::EnterDir)) {
+ case WalkPath::Action::Abort:
+ return false;
+ case WalkPath::Action::SkipDir:
+ return true;
+ case WalkPath::Action::Continue:
+ break;
+ }
+ auto status = walk_path_subdir(path, subdir, func);
+ if (status.is_error() || !status.ok()) {
+ return status;
+ }
+ switch (func(path, WalkPath::Type::ExitDir)) {
+ case WalkPath::Action::Abort:
+ return false;
+ case WalkPath::Action::SkipDir:
+ case WalkPath::Action::Continue:
+ break;
+ }
+ return true;
+}
+
+Result<bool> walk_path_dir(string &path, FileFd fd, const WalkFunction &func) {
+ auto native_fd = fd.move_as_native_fd();
+ auto *subdir = fdopendir(native_fd.fd());
+ if (subdir == nullptr) {
+ return OS_ERROR("fdopendir");
+ }
+ native_fd.release();
+ return walk_path_dir(path, subdir, func);
+}
+
+Result<bool> walk_path_dir(string &path, const WalkFunction &func) {
+ auto *subdir = opendir(path.c_str());
+ if (subdir == nullptr) {
+ return OS_ERROR(PSLICE() << tag("opendir", path));
+ }
+ return walk_path_dir(path, subdir, func);
+}
+
+Result<bool> walk_path_file(string &path, const WalkFunction &func) {
+ switch (func(path, WalkPath::Type::NotDir)) {
+ case WalkPath::Action::Abort:
+ return false;
+ case WalkPath::Action::SkipDir:
+ case WalkPath::Action::Continue:
+ break;
+ }
+ return true;
+}
+
+Result<bool> walk_path(string &path, const WalkFunction &func) {
+ TRY_RESULT(fd, FileFd::open(path, FileFd::Read));
+ TRY_RESULT(stat, fd.stat());
+
+ bool is_dir = stat.is_dir_;
+ bool is_reg = stat.is_reg_;
+ if (is_dir) {
+ return walk_path_dir(path, std::move(fd), func);
+ }
+
+ fd.close();
+ if (is_reg) {
+ return walk_path_file(path, func);
+ }
+
+ return true;
+}
+} // namespace detail
+
+Status WalkPath::do_run(CSlice path, const detail::WalkFunction &func) {
+ string curr_path;
+ curr_path.reserve(PATH_MAX + 10);
+ curr_path = path.c_str();
+ TRY_STATUS(detail::walk_path(curr_path, func));
+ return Status::OK();
+}
+
#endif
#if TD_PORT_WINDOWS
Status mkdir(CSlice dir, int32 mode) {
TRY_RESULT(wdir, to_wstring(dir));
- auto status = CreateDirectoryW(wdir.c_str(), nullptr);
+ while (!wdir.empty() && (wdir.back() == L'/' || wdir.back() == L'\\')) {
+ wdir.pop_back();
+ }
+ auto status = td::CreateDirectoryFromAppW(wdir.c_str(), nullptr);
if (status == 0 && GetLastError() != ERROR_ALREADY_EXISTS) {
return OS_ERROR(PSLICE() << "Can't create directory \"" << dir << '"');
}
@@ -232,7 +410,7 @@ Status mkdir(CSlice dir, int32 mode) {
Status rename(CSlice from, CSlice to) {
TRY_RESULT(wfrom, to_wstring(from));
TRY_RESULT(wto, to_wstring(to));
- auto status = MoveFileExW(wfrom.c_str(), wto.c_str(), MOVEFILE_REPLACE_EXISTING);
+ auto status = td::MoveFileExFromAppW(wfrom.c_str(), wto.c_str(), MOVEFILE_REPLACE_EXISTING);
if (status == 0) {
return OS_ERROR(PSLICE() << "Can't rename \"" << from << "\" to \"" << to << '\"');
}
@@ -251,8 +429,7 @@ Result<string> realpath(CSlice slice, bool ignore_access_denied) {
return OS_ERROR(PSLICE() << "GetFullPathNameW failed for \"" << slice << '"');
}
} else {
- TRY_RESULT(t_res, from_wstring(buf));
- res = std::move(t_res);
+ TRY_RESULT_ASSIGN(res, from_wstring(buf));
}
if (res.empty()) {
return Status::Error("Empty path");
@@ -276,7 +453,7 @@ Status chdir(CSlice dir) {
Status rmdir(CSlice dir) {
TRY_RESULT(wdir, to_wstring(dir));
- int status = RemoveDirectoryW(wdir.c_str());
+ int status = td::RemoveDirectoryFromAppW(wdir.c_str());
if (!status) {
return OS_ERROR(PSLICE() << "Can't delete directory \"" << dir << '"');
}
@@ -285,7 +462,7 @@ Status rmdir(CSlice dir) {
Status unlink(CSlice path) {
TRY_RESULT(wpath, to_wstring(path));
- int status = DeleteFileW(wpath.c_str());
+ int status = td::DeleteFileFromAppW(wpath.c_str());
if (!status) {
return OS_ERROR(PSLICE() << "Can't unlink \"" << path << '"');
}
@@ -302,7 +479,7 @@ CSlice get_temporary_dir() {
}
auto rs = from_wstring(buf);
LOG_IF(FATAL, rs.is_error()) << "GetTempPathW failed: " << rs.error();
- temporary_dir = rs.ok();
+ temporary_dir = rs.move_as_ok();
}
if (temporary_dir.size() > 1 && temporary_dir.back() == TD_DIR_SLASH) {
temporary_dir.pop_back();
@@ -332,9 +509,9 @@ Result<string> mkdtemp(CSlice dir, Slice prefix) {
}
dir_pattern.append(prefix.begin(), prefix.size());
- for (auto it = 0; it < 20; it++) {
+ for (auto iter = 0; iter < 20; iter++) {
auto path = dir_pattern;
- for (int i = 0; i < 6 + it / 5; i++) {
+ for (int i = 0; i < 6 + iter / 5; i++) {
path += static_cast<char>(Random::fast('a', 'z'));
}
auto status = mkdir(path);
@@ -364,9 +541,9 @@ Result<std::pair<FileFd, string>> mkstemp(CSlice dir) {
}
file_pattern += "tmp";
- for (auto it = 0; it < 20; it++) {
+ for (auto iter = 0; iter < 20; iter++) {
auto path = file_pattern;
- for (int i = 0; i < 6 + it / 5; i++) {
+ for (int i = 0; i < 6 + iter / 5; i++) {
path += static_cast<char>(Random::fast('a', 'z'));
}
auto r_file = FileFd::open(path, FileFd::Write | FileFd::Read | FileFd::CreateNew);
@@ -378,6 +555,79 @@ Result<std::pair<FileFd, string>> mkstemp(CSlice dir) {
return Status::Error(PSLICE() << "Can't create temporary file \"" << file_pattern << '"');
}
+static Result<bool> walk_path_dir(const std::wstring &dir_name,
+ const std::function<WalkPath::Action(CSlice name, WalkPath::Type type)> &func) {
+ std::wstring name = dir_name + L"\\*";
+ WIN32_FIND_DATA file_data;
+ auto handle =
+ td::FindFirstFileExFromAppW(name.c_str(), FindExInfoStandard, &file_data, FindExSearchNameMatch, nullptr, 0);
+ if (handle == INVALID_HANDLE_VALUE) {
+ return OS_ERROR(PSLICE() << "FindFirstFileEx" << tag("name", from_wstring(name).ok()));
+ }
+
+ SCOPE_EXIT {
+ FindClose(handle);
+ };
+
+ TRY_RESULT(dir_entry_name, from_wstring(dir_name));
+ switch (func(dir_entry_name, WalkPath::Type::EnterDir)) {
+ case WalkPath::Action::Abort:
+ return false;
+ case WalkPath::Action::SkipDir:
+ return true;
+ case WalkPath::Action::Continue:
+ break;
+ }
+
+ while (true) {
+ auto full_name = dir_name + L"\\" + file_data.cFileName;
+ TRY_RESULT(entry_name, from_wstring(full_name));
+ if (file_data.cFileName[0] != '.') {
+ if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ TRY_RESULT(is_ok, walk_path_dir(full_name, func));
+ if (!is_ok) {
+ return false;
+ }
+ } else {
+ switch (func(entry_name, WalkPath::Type::NotDir)) {
+ case WalkPath::Action::Abort:
+ return false;
+ case WalkPath::Action::SkipDir:
+ case WalkPath::Action::Continue:
+ break;
+ }
+ }
+ }
+ auto status = FindNextFileW(handle, &file_data);
+ if (status == 0) {
+ auto last_error = GetLastError();
+ if (last_error == ERROR_NO_MORE_FILES) {
+ break;
+ }
+ return OS_ERROR("FindNextFileW");
+ }
+ }
+ switch (func(dir_entry_name, WalkPath::Type::ExitDir)) {
+ case WalkPath::Action::Abort:
+ return false;
+ case WalkPath::Action::SkipDir:
+ case WalkPath::Action::Continue:
+ break;
+ }
+ return true;
+}
+
+Status WalkPath::do_run(CSlice path, const std::function<Action(CSlice name, Type)> &func) {
+ TRY_RESULT(wpath, to_wstring(path));
+ Slice path_slice = path;
+ while (!path_slice.empty() && (path_slice.back() == '/' || path_slice.back() == '\\')) {
+ path_slice.remove_suffix(1);
+ wpath.pop_back();
+ }
+ TRY_STATUS(walk_path_dir(wpath, func));
+ return Status::OK();
+}
+
#endif
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.h
index 47b7d3a350..0ac73ccb31 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/path.h
@@ -1,225 +1,72 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/port/config.h"
-
#include "td/utils/common.h"
-#include "td/utils/format.h"
-#include "td/utils/logging.h"
#include "td/utils/port/FileFd.h"
-#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
+#include <functional>
+#include <type_traits>
#include <utility>
-#if TD_PORT_POSIX
-#include <dirent.h>
-#include <sys/types.h>
-#endif
-
-#if TD_DARWIN
-#include <sys/syslimits.h>
-#endif
-
-#if TD_PORT_WINDOWS
-#include "td/utils/port/wstring_convert.h"
-
-#include <string>
-#endif
-
namespace td {
Status mkdir(CSlice dir, int32 mode = 0700) TD_WARN_UNUSED_RESULT;
+
Status mkpath(CSlice path, int32 mode = 0700) TD_WARN_UNUSED_RESULT;
+
Status rename(CSlice from, CSlice to) TD_WARN_UNUSED_RESULT;
+
Result<string> realpath(CSlice slice, bool ignore_access_denied = false) TD_WARN_UNUSED_RESULT;
+
Status chdir(CSlice dir) TD_WARN_UNUSED_RESULT;
+
Status rmdir(CSlice dir) TD_WARN_UNUSED_RESULT;
+
Status unlink(CSlice path) TD_WARN_UNUSED_RESULT;
-Status set_temporary_dir(CSlice dir) TD_WARN_UNUSED_RESULT;
-CSlice get_temporary_dir();
-Result<std::pair<FileFd, string>> mkstemp(CSlice dir) TD_WARN_UNUSED_RESULT;
-Result<string> mkdtemp(CSlice dir, Slice prefix) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path(CSlice path, Func &func) TD_WARN_UNUSED_RESULT;
-
-#if TD_PORT_POSIX
-
-// TODO move details somewhere else
-namespace detail {
-template <class Func>
-Status walk_path_dir(string &path, FileFd fd, Func &&func) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path_dir(string &path, Func &&func) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path_file(string &path, Func &&func) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path(string &path, Func &&func) TD_WARN_UNUSED_RESULT;
-
-template <class Func>
-Status walk_path_subdir(string &path, DIR *dir, Func &&func) {
- while (true) {
- errno = 0;
- auto *entry = readdir(dir);
- auto readdir_errno = errno;
- if (readdir_errno) {
- return Status::PosixError(readdir_errno, "readdir");
- }
- if (entry == nullptr) {
- return Status::OK();
- }
- Slice name = Slice(&*entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
- auto size = path.size();
- if (path.back() != TD_DIR_SLASH) {
- path += TD_DIR_SLASH;
- }
- path.append(name.begin(), name.size());
- SCOPE_EXIT {
- path.resize(size);
- };
- Status status;
-#ifdef DT_DIR
- if (entry->d_type == DT_UNKNOWN) {
- status = walk_path(path, std::forward<Func>(func));
- } else if (entry->d_type == DT_DIR) {
- status = walk_path_dir(path, std::forward<Func>(func));
- } else if (entry->d_type == DT_REG) {
- status = walk_path_file(path, std::forward<Func>(func));
- }
-#else
-#warning "Slow walk_path"
- status = walk_path(path, std::forward<Func>(func));
-#endif
- if (status.is_error()) {
- return status;
- }
- }
-}
+Status rmrf(CSlice path) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path_dir(string &path, DIR *subdir, Func &&func) {
- SCOPE_EXIT {
- closedir(subdir);
- };
- TRY_STATUS(walk_path_subdir(path, subdir, std::forward<Func>(func)));
- std::forward<Func>(func)(path, true);
- return Status::OK();
-}
+Status set_temporary_dir(CSlice dir) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path_dir(string &path, FileFd fd, Func &&func) {
- auto *subdir = fdopendir(fd.get_fd().move_as_native_fd());
- if (subdir == nullptr) {
- auto error = OS_ERROR("fdopendir");
- fd.close();
- return error;
- }
- return walk_path_dir(path, subdir, std::forward<Func>(func));
-}
+CSlice get_temporary_dir();
-template <class Func>
-Status walk_path_dir(string &path, Func &&func) {
- auto *subdir = opendir(path.c_str());
- if (subdir == nullptr) {
- return OS_ERROR(PSLICE() << tag("opendir", path));
- }
- return walk_path_dir(path, subdir, std::forward<Func>(func));
-}
+Result<std::pair<FileFd, string>> mkstemp(CSlice dir) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path_file(string &path, Func &&func) {
- std::forward<Func>(func)(path, false);
- return Status::OK();
-}
+Result<string> mkdtemp(CSlice dir, Slice prefix) TD_WARN_UNUSED_RESULT;
-template <class Func>
-Status walk_path(string &path, Func &&func) {
- TRY_RESULT(fd, FileFd::open(path, FileFd::Read));
- auto stat = fd.stat();
- bool is_dir = stat.is_dir_;
- bool is_reg = stat.is_reg_;
- if (is_dir) {
- return walk_path_dir(path, std::move(fd), std::forward<Func>(func));
- }
+class WalkPath {
+ public:
+ enum class Action { Continue, Abort, SkipDir };
+ enum class Type { EnterDir, ExitDir, NotDir };
- fd.close();
- if (is_reg) {
- return walk_path_file(path, std::forward<Func>(func));
+ template <class F, class R = decltype(std::declval<F>()("", Type::ExitDir))>
+ static TD_WARN_UNUSED_RESULT std::enable_if_t<std::is_same<R, Action>::value, Status> run(CSlice path, F &&func) {
+ return do_run(path, func);
}
-
- return Status::OK();
-}
-} // namespace detail
-
-template <class Func>
-Status walk_path(CSlice path, Func &&func) {
- string curr_path;
- curr_path.reserve(PATH_MAX + 10);
- curr_path = path.c_str();
- return detail::walk_path(curr_path, std::forward<Func>(func));
-}
-
-#endif
-
-#if TD_PORT_WINDOWS
-
-namespace detail {
-template <class Func>
-Status walk_path_dir(const std::wstring &dir_name, Func &&func) {
- std::wstring name = dir_name + L"\\*";
-
- WIN32_FIND_DATA file_data;
- auto handle = FindFirstFileExW(name.c_str(), FindExInfoStandard, &file_data, FindExSearchNameMatch, nullptr, 0);
- if (handle == INVALID_HANDLE_VALUE) {
- return OS_ERROR(PSLICE() << "FindFirstFileEx" << tag("name", from_wstring(name).ok()));
+ template <class F, class R = decltype(std::declval<F>()("", Type::ExitDir))>
+ static TD_WARN_UNUSED_RESULT std::enable_if_t<!std::is_same<R, Action>::value, Status> run(CSlice path, F &&func) {
+ return do_run(path, [&](CSlice name, Type type) {
+ func(name, type);
+ return Action::Continue;
+ });
}
- SCOPE_EXIT {
- FindClose(handle);
- };
- while (true) {
- auto full_name = dir_name + L"\\" + file_data.cFileName;
- TRY_RESULT(entry_name, from_wstring(full_name));
- if (file_data.cFileName[0] != '.') {
- if ((file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
- TRY_STATUS(walk_path_dir(full_name, func));
- func(entry_name, true);
- } else {
- func(entry_name, false);
- }
- }
- auto status = FindNextFileW(handle, &file_data);
- if (status == 0) {
- auto last_error = GetLastError();
- if (last_error == ERROR_NO_MORE_FILES) {
- return Status::OK();
- }
- return OS_ERROR("FindNextFileW");
- }
- }
-}
-} // namespace detail
+ private:
+ static TD_WARN_UNUSED_RESULT Status do_run(CSlice path,
+ const std::function<WalkPath::Action(CSlice name, Type type)> &func);
+};
-template <class Func>
-Status walk_path(CSlice path, Func &&func) {
- Slice path_slice = path;
- while (!path_slice.empty() && (path_slice.back() == '/' || path_slice.back() == '\\')) {
- path_slice.remove_suffix(1);
- }
- TRY_RESULT(wpath, to_wstring(path_slice));
- return detail::walk_path_dir(wpath.c_str(), func);
+// deprecated interface
+template <class F>
+TD_WARN_UNUSED_RESULT Status walk_path(CSlice path, F &&func) {
+ return WalkPath::run(path, func);
}
-#endif
-
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.cpp
new file mode 100644
index 0000000000..9fdd36528e
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.cpp
@@ -0,0 +1,25 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/platform.h"
+
+char disable_linker_warning_about_empty_file_platform_cpp TD_UNUSED;
+
+#if !TD_MSVC
+
+#if TD_DARWIN_UNKNOWN
+#warning "Probably unsupported Apple operating system. Feel free to try to compile"
+#endif
+
+#if TD_UNIX_UNKNOWN
+#warning "Probably unsupported Unix operating system. Feel free to try to compile"
+#endif
+
+#if TD_COMPILER_UNKNOWN
+#warning "Probably unsupported compiler. Feel free to try to compile"
+#endif
+
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.h
index a1c3776a40..0d896ff3c2 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/platform.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,7 +9,7 @@
// clang-format off
/*** Platform macros ***/
-#if defined(_WIN32)
+#if defined(_WIN32) || defined(_WINDOWS) // _WINDOWS is defined by CMake
#if defined(__cplusplus_winrt)
#define TD_WINRT 1
#endif
@@ -20,7 +20,7 @@
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#if TARGET_OS_IPHONE
- // iOS/Apple Watch OS/Apple TV OS
+ // iOS/iPadOS/watchOS/tvOS
#if TARGET_OS_IOS
#define TD_DARWIN_IOS 1
#elif TARGET_OS_TV
@@ -28,13 +28,13 @@
#elif TARGET_OS_WATCH
#define TD_DARWIN_WATCH_OS 1
#else
- #warning "Probably unsupported Apple iPhone platform. Feel free to try to compile"
+ #define TD_DARWIN_UNKNOWN 1
#endif
#elif TARGET_OS_MAC
- // Other kinds of Mac OS
+ // Other kinds of macOS
#define TD_DARWIN_MAC 1
#else
- #warning "Probably unsupported Apple platform. Feel free to try to compile"
+ #define TD_DARWIN_UNKNOWN 1
#endif
#define TD_DARWIN 1
#elif defined(ANDROID) || defined(__ANDROID__)
@@ -43,12 +43,21 @@
#define TD_TIZEN 1
#elif defined(__linux__)
#define TD_LINUX 1
+#elif defined(__FreeBSD__)
+ #define TD_FREEBSD 1
+#elif defined(__OpenBSD__)
+ #define TD_OPENBSD 1
+#elif defined(__NetBSD__)
+ #define TD_NETBSD 1
#elif defined(__CYGWIN__)
#define TD_CYGWIN 1
#elif defined(__EMSCRIPTEN__)
#define TD_EMSCRIPTEN 1
+#elif defined(__sun)
+ #define TD_SOLARIS 1
+ // TD_ILLUMOS can be already defined by CMake
#elif defined(__unix__) // all unices not caught above
- #warning "Probably unsupported Unix platform. Feel free to try to compile"
+ #define TD_UNIX_UNKNOWN 1
#define TD_CYGWIN 1
#else
#error "Probably unsupported platform. Feel free to remove the error and try to recompile"
@@ -63,7 +72,7 @@
#elif defined(_MSC_VER)
#define TD_MSVC 1
#else
- #warning "Probably unsupported compiler. Feel free to try to compile"
+ #define TD_COMPILER_UNKNOWN 1
#endif
#if TD_GCC || TD_CLANG || TD_INTEL
@@ -103,4 +112,8 @@
#define TD_CONCURRENCY_PAD 128
+#if !TD_WINDOWS && defined(__SIZEOF_INT128__)
+#define TD_HAVE_INT128 1
+#endif
+
// clang-format on
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.cpp
new file mode 100644
index 0000000000..8bc94dbfa2
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.cpp
@@ -0,0 +1,97 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/rlimit.h"
+
+#include "td/utils/port/config.h"
+
+#include "td/utils/misc.h"
+
+#if TD_PORT_POSIX
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#endif
+
+namespace td {
+
+#if TD_PORT_POSIX
+static int get_resource(ResourceLimitType type) {
+ switch (type) {
+ case ResourceLimitType::NoFile:
+ return RLIMIT_NOFILE;
+ default:
+ UNREACHABLE();
+ return -1;
+ }
+}
+#endif
+
+Status set_resource_limit(ResourceLimitType type, uint64 value, uint64 max_value) {
+#if TD_PORT_POSIX
+ if (max_value != 0 && value > max_value) {
+ return Status::Error("New resource limit value must not be bigger than max_value");
+ }
+
+ int resource = get_resource(type);
+
+ rlimit rlim;
+ if (getrlimit(resource, &rlim) == -1) {
+ return OS_ERROR("Failed to get current resource limit");
+ }
+
+ TRY_RESULT(new_value, narrow_cast_safe<rlim_t>(value));
+ TRY_RESULT(new_max_value, narrow_cast_safe<rlim_t>(max_value));
+ if (new_max_value) {
+ rlim.rlim_max = new_max_value;
+ } else if (rlim.rlim_max < new_value) {
+ rlim.rlim_max = new_value;
+ }
+ rlim.rlim_cur = new_value;
+
+ if (setrlimit(resource, &rlim) < 0) {
+ return OS_ERROR("Failed to set resource limit");
+ }
+ return Status::OK();
+#elif TD_PORT_WINDOWS
+ return Status::OK(); // Windows has no limits
+#endif
+}
+
+Status set_maximize_resource_limit(ResourceLimitType type, uint64 value) {
+#if TD_PORT_POSIX
+ int resource = get_resource(type);
+
+ rlimit rlim;
+ if (getrlimit(resource, &rlim) == -1) {
+ return OS_ERROR("Failed to get current resource limit");
+ }
+
+ TRY_RESULT(new_value, narrow_cast_safe<rlim_t>(value));
+ if (rlim.rlim_max < new_value) {
+ // trying to increase rlim_max
+ rlimit new_rlim;
+ new_rlim.rlim_cur = new_value;
+ new_rlim.rlim_max = new_value;
+ if (setrlimit(resource, &new_rlim) >= 0) {
+ return Status::OK();
+ }
+
+ // do not increase rlim_max if have no rights
+ new_value = rlim.rlim_max;
+ }
+ rlim.rlim_cur = new_value;
+
+ if (setrlimit(resource, &rlim) < 0) {
+ return OS_ERROR("Failed to set resource limit");
+ }
+ return Status::OK();
+#elif TD_PORT_WINDOWS
+ return Status::OK(); // Windows has no limits
+#endif
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.h
new file mode 100644
index 0000000000..a987a7bdac
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/rlimit.h
@@ -0,0 +1,20 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Status.h"
+
+namespace td {
+
+enum class ResourceLimitType { NoFile };
+
+Status set_resource_limit(ResourceLimitType type, uint64 value, uint64 max_value = 0);
+
+Status set_maximize_resource_limit(ResourceLimitType type, uint64 value);
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.cpp
index 8627474d63..d61cf69d94 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,9 +7,11 @@
#include "td/utils/port/signals.h"
#include "td/utils/port/config.h"
+#include "td/utils/port/stacktrace.h"
+#include "td/utils/port/StdStreams.h"
+#include "td/utils/common.h"
#include "td/utils/format.h"
-#include "td/utils/logging.h"
#if TD_PORT_POSIX
#include <signal.h>
@@ -22,6 +24,7 @@
#include <cerrno>
#include <cstdint>
+#include <cstdlib>
#include <cstring>
#include <ctime>
#include <limits>
@@ -63,23 +66,24 @@ Status setup_signals_alt_stack() {
}
#if TD_PORT_POSIX
+static void set_handler(struct sigaction &act, decltype(act.sa_handler) handler) {
+ act.sa_handler = handler;
+}
+static void set_handler(struct sigaction &act, decltype(act.sa_sigaction) handler) {
+ act.sa_sigaction = handler;
+ act.sa_flags |= SA_SIGINFO;
+}
template <class F>
-static Status set_signal_handler_impl(vector<int> signals, F func, bool is_extended = false) {
+static Status set_signal_handler_impl(vector<int> &&signals, F func) {
struct sigaction act;
std::memset(&act, '\0', sizeof(act));
- if (is_extended) { // TODO if constexpr, remove useless reinterpret_cast
- act.sa_handler = reinterpret_cast<decltype(act.sa_handler)>(func);
- } else {
- act.sa_sigaction = reinterpret_cast<decltype(act.sa_sigaction)>(func);
- }
+
sigemptyset(&act.sa_mask);
for (auto signal : signals) {
sigaddset(&act.sa_mask, signal);
}
act.sa_flags = SA_RESTART | SA_ONSTACK;
- if (is_extended) {
- act.sa_flags |= SA_SIGINFO;
- }
+ set_handler(act, func);
for (auto signal : signals) {
if (sigaction(signal, &act, nullptr) != 0) {
@@ -109,10 +113,23 @@ static vector<int> get_native_signals(SignalType type) {
return {};
}
}
-#endif
-#if TD_PORT_WINDOWS
-static Status set_signal_handler_impl(vector<int> signals, void (*func)(int sig), bool /*unused*/ = true) {
+#elif TD_PORT_WINDOWS
+using signal_handler = void (*)(int sig);
+static signal_handler signal_handlers[NSIG] = {};
+
+static void signal_handler_func(int sig) {
+ std::signal(sig, signal_handler_func);
+ auto handler = signal_handlers[sig];
+ handler(sig);
+}
+
+static Status set_signal_handler_impl(vector<int> &&signals, void (*func)(int sig)) {
for (auto signal : signals) {
+ CHECK(0 <= signal && signal < NSIG);
+ if (func != SIG_IGN && func != SIG_DFL) {
+ signal_handlers[signal] = func;
+ func = signal_handler_func;
+ }
if (std::signal(signal, func) == SIG_ERR) {
return Status::Error("Failed to set signal handler");
}
@@ -142,7 +159,7 @@ static vector<int> get_native_signals(SignalType type) {
}
#endif
-Status set_signal_handler(SignalType type, void (*func)(int)) {
+Status set_signal_handler(SignalType type, void (*func)(int sig)) {
return set_signal_handler_impl(get_native_signals(type), func == nullptr ? SIG_DFL : func);
}
@@ -171,13 +188,13 @@ Status set_extended_signal_handler(SignalType type, extended_signal_handler func
UNREACHABLE();
}
}
- return set_signal_handler_impl(std::move(signals), siginfo_handler, true);
+ return set_signal_handler_impl(std::move(signals), siginfo_handler);
}
-Status set_runtime_signal_handler(int runtime_signal_number, void (*func)(int)) {
+Status set_real_time_signal_handler(int real_time_signal_number, void (*func)(int)) {
#ifdef SIGRTMIN
- CHECK(SIGRTMIN + runtime_signal_number <= SIGRTMAX);
- return set_signal_handler_impl({SIGRTMIN + runtime_signal_number}, func == nullptr ? SIG_DFL : func);
+ CHECK(SIGRTMIN + real_time_signal_number <= SIGRTMAX);
+ return set_signal_handler_impl({SIGRTMIN + real_time_signal_number}, func == nullptr ? SIG_DFL : func);
#else
return Status::OK();
#endif
@@ -279,13 +296,13 @@ void signal_safe_write_signal_number(int sig, bool add_header) {
}
void signal_safe_write_pointer(void *p, bool add_header) {
- std::uintptr_t addr = reinterpret_cast<std::uintptr_t>(p);
+ auto addr = reinterpret_cast<std::uintptr_t>(p);
char buf[100];
char *end = buf + sizeof(buf);
char *ptr = end;
*--ptr = '\n';
do {
- *--ptr = td::format::hex_digit(addr % 16);
+ *--ptr = format::hex_digit(addr % 16);
addr /= 16;
} while (addr != 0);
*--ptr = 'x';
@@ -295,4 +312,33 @@ void signal_safe_write_pointer(void *p, bool add_header) {
signal_safe_write(Slice(ptr, end), add_header);
}
+static void block_stdin() {
+#if TD_PORT_POSIX
+ Stdin().get_native_fd().set_is_blocking(true).ignore();
+#endif
+}
+
+static void default_failure_signal_handler(int sig) {
+ Stacktrace::init();
+ signal_safe_write_signal_number(sig);
+
+ Stacktrace::PrintOptions options;
+ options.use_gdb = true;
+ Stacktrace::print_to_stderr(options);
+
+ block_stdin();
+ _Exit(EXIT_FAILURE);
+}
+
+Status set_default_failure_signal_handler() {
+#if TD_PORT_POSIX
+ Stdin(); // init static variables before atexit
+ std::atexit(block_stdin);
+#endif
+ TRY_STATUS(setup_signals_alt_stack());
+ TRY_STATUS(set_signal_handler(SignalType::Abort, default_failure_signal_handler));
+ TRY_STATUS(set_signal_handler(SignalType::Error, default_failure_signal_handler));
+ return Status::OK();
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.h
index 1f6ed24732..fa5fbae976 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/signals.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -20,7 +20,7 @@ Status set_signal_handler(SignalType type, void (*func)(int sig)) TD_WARN_UNUSED
Status set_extended_signal_handler(SignalType type, void (*func)(int sig, void *addr)) TD_WARN_UNUSED_RESULT;
-Status set_runtime_signal_handler(int runtime_signal_number, void (*func)(int sig)) TD_WARN_UNUSED_RESULT;
+Status set_real_time_signal_handler(int real_time_signal_number, void (*func)(int sig)) TD_WARN_UNUSED_RESULT;
Status ignore_signal(SignalType type) TD_WARN_UNUSED_RESULT;
@@ -31,4 +31,6 @@ void signal_safe_write_signal_number(int sig, bool add_header = true);
void signal_safe_write_pointer(void *p, bool add_header = true);
+Status set_default_failure_signal_handler();
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.cpp
index 4f02f69dfb..c35de1f2ad 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.h
index 56cd9f7bcd..2f9d27a3d5 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/sleep.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.cpp
new file mode 100644
index 0000000000..7fd07b2a62
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.cpp
@@ -0,0 +1,139 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/stacktrace.h"
+
+#include "td/utils/port/signals.h"
+
+#if __GLIBC__
+#include <execinfo.h>
+#endif
+
+#if TD_LINUX || TD_FREEBSD
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if TD_LINUX
+#include <sys/prctl.h>
+#endif
+#endif
+
+namespace td {
+
+namespace {
+
+void print_backtrace() {
+#if TD_PORT_WINDOWS
+ void *buffer[128];
+ USHORT nptrs = CaptureStackBackTrace(0, 128, buffer, nullptr);
+#elif __GLIBC__
+ void *buffer[128];
+ int nptrs = backtrace(buffer, 128);
+#else
+ return;
+#endif
+
+ signal_safe_write("------- Stack Backtrace -------\n", false);
+#if TD_PORT_WINDOWS
+ for (USHORT i = 0; i < nptrs; i++) {
+ signal_safe_write_pointer(buffer[i], false);
+ }
+#elif __GLIBC__
+ backtrace_symbols_fd(buffer, nptrs, 2);
+#endif
+ signal_safe_write("-------------------------------\n", false);
+}
+
+void print_backtrace_gdb() {
+#if TD_LINUX || TD_FREEBSD
+ char pid_buf[30];
+ char *pid_buf_begin = pid_buf + sizeof(pid_buf);
+ pid_t pid = getpid();
+ *--pid_buf_begin = '\0';
+ do {
+ *--pid_buf_begin = static_cast<char>(pid % 10 + '0');
+ pid /= 10;
+ } while (pid > 0);
+
+ char name_buf[512];
+ ssize_t res = readlink("/proc/self/exe", name_buf, 511); // TODO works only under Linux
+ if (res >= 0) {
+ name_buf[res] = 0;
+
+#if TD_LINUX
+#if defined(PR_SET_DUMPABLE)
+ if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
+ signal_safe_write("Can't set dumpable\n");
+ return;
+ }
+#endif
+#if defined(PR_SET_PTRACER)
+ // We can't use event fd because we are in a signal handler
+ int fds[2];
+ bool need_set_ptracer = true;
+ if (pipe(fds) < 0) {
+ need_set_ptracer = false;
+ signal_safe_write("Can't create a pipe\n");
+ }
+#endif
+#endif
+
+ int child_pid = fork();
+ if (child_pid < 0) {
+ signal_safe_write("Can't fork() to run gdb\n");
+ return;
+ }
+ if (!child_pid) {
+#if TD_LINUX && defined(PR_SET_PTRACER)
+ if (need_set_ptracer) {
+ char c;
+ if (read(fds[0], &c, 1) < 0) {
+ signal_safe_write("Failed to read from pipe\n");
+ }
+ }
+#endif
+ dup2(2, 1); // redirect output to stderr
+ execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "thread apply all bt full", name_buf, pid_buf_begin,
+ nullptr);
+ return;
+ } else {
+#if TD_LINUX && defined(PR_SET_PTRACER)
+ if (need_set_ptracer) {
+ if (prctl(PR_SET_PTRACER, child_pid, 0, 0, 0) < 0) {
+ signal_safe_write("Can't set ptracer\n");
+ }
+ if (write(fds[1], "a", 1) != 1) {
+ signal_safe_write("Can't write to pipe\n");
+ }
+ }
+#endif
+ waitpid(child_pid, nullptr, 0);
+ }
+ } else {
+ signal_safe_write("Can't get name of executable file to pass to gdb\n");
+ }
+#endif
+}
+
+} // namespace
+
+void Stacktrace::print_to_stderr(const PrintOptions &options) {
+ print_backtrace();
+ if (options.use_gdb) {
+ print_backtrace_gdb();
+ }
+}
+
+void Stacktrace::init() {
+#if __GLIBC__
+ // backtrace needs to be called once to ensure that next calls are async-signal-safe
+ void *buffer[1];
+ backtrace(buffer, 1);
+#endif
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.h
new file mode 100644
index 0000000000..469a20dfe6
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/stacktrace.h
@@ -0,0 +1,23 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+namespace td {
+
+class Stacktrace {
+ public:
+ struct PrintOptions {
+ bool use_gdb = false;
+ PrintOptions() {
+ }
+ };
+ static void print_to_stderr(const PrintOptions &options = PrintOptions());
+
+ static void init();
+};
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread.h
index 3034e456e8..35053a32fb 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -22,9 +22,6 @@ namespace td {
using thread = detail::ThreadStl;
namespace this_thread = detail::this_thread_stl;
#elif TD_THREAD_UNSUPPORTED
- namespace this_thread {
- inline void yield() {}
- }
#else
#error "Thread's implementation is not defined"
#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.cpp
index aa4e371405..ee56a119cf 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.cpp
@@ -1,23 +1,21 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/port/thread_local.h"
-#include "td/utils/logging.h"
-
namespace td {
namespace detail {
static TD_THREAD_LOCAL int32 thread_id_;
-static TD_THREAD_LOCAL std::vector<std::unique_ptr<Guard>> *thread_local_destructors;
+static TD_THREAD_LOCAL std::vector<unique_ptr<Destructor>> *thread_local_destructors;
-void add_thread_local_destructor(std::unique_ptr<Guard> destructor) {
+void add_thread_local_destructor(unique_ptr<Destructor> destructor) {
if (thread_local_destructors == nullptr) {
- thread_local_destructors = new std::vector<std::unique_ptr<Guard>>();
+ thread_local_destructors = new std::vector<unique_ptr<Destructor>>();
}
thread_local_destructors->push_back(std::move(destructor));
}
@@ -31,6 +29,7 @@ void clear_thread_locals() {
delete to_delete;
CHECK(detail::thread_local_destructors == nullptr);
}
+
void set_thread_id(int32 id) {
detail::thread_id_ = id;
}
@@ -38,4 +37,5 @@ void set_thread_id(int32 id) {
int32 get_thread_id() {
return detail::thread_id_;
}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.h
index 6d8c135e88..deaeb09508 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/thread_local.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,7 +9,7 @@
#include "td/utils/port/config.h"
#include "td/utils/common.h"
-#include "td/utils/ScopeGuard.h"
+#include "td/utils/Destructor.h"
#include <memory>
#include <utility>
@@ -27,13 +27,9 @@ namespace td {
#endif
// clang-format on
-inline constexpr size_t max_thread_count() {
- return 256;
-}
-
// If raw_ptr is not nullptr, allocate T as in std::make_unique<T>(args...) and store pointer into raw_ptr
template <class T, class P, class... ArgsT>
-bool init_thread_local(P &raw_ptr, ArgsT &&... args);
+bool init_thread_local(P &raw_ptr, ArgsT &&...args);
// Destroy all thread locals, and store nullptr into corresponding pointers
void clear_thread_locals();
@@ -43,14 +39,14 @@ void set_thread_id(int32 id);
int32 get_thread_id();
namespace detail {
-void add_thread_local_destructor(std::unique_ptr<Guard> destructor);
+void add_thread_local_destructor(unique_ptr<Destructor> destructor);
template <class T, class P, class... ArgsT>
-void do_init_thread_local(P &raw_ptr, ArgsT &&... args) {
+void do_init_thread_local(P &raw_ptr, ArgsT &&...args) {
auto ptr = std::make_unique<T>(std::forward<ArgsT>(args)...);
raw_ptr = ptr.get();
- detail::add_thread_local_destructor(create_lambda_guard([ptr = std::move(ptr), &raw_ptr]() mutable {
+ detail::add_thread_local_destructor(create_destructor([ptr = std::move(ptr), &raw_ptr]() mutable {
ptr.reset();
raw_ptr = nullptr;
}));
@@ -58,7 +54,7 @@ void do_init_thread_local(P &raw_ptr, ArgsT &&... args) {
} // namespace detail
template <class T, class P, class... ArgsT>
-bool init_thread_local(P &raw_ptr, ArgsT &&... args) {
+bool init_thread_local(P &raw_ptr, ArgsT &&...args) {
if (likely(raw_ptr != nullptr)) {
return false;
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/uname.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/uname.cpp
new file mode 100644
index 0000000000..5c380b5e05
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/uname.cpp
@@ -0,0 +1,289 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/uname.h"
+
+#include "td/utils/port/config.h"
+
+#include "td/utils/common.h"
+#include "td/utils/filesystem.h"
+#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/Stat.h"
+#include "td/utils/SliceBuilder.h"
+
+#if TD_PORT_POSIX
+
+#include <cstring>
+
+#if TD_ANDROID
+#include <sys/system_properties.h>
+#elif TD_EMSCRIPTEN
+#include <cstdlib>
+
+#include <emscripten.h>
+#else
+#if TD_DARWIN
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#endif
+#include <sys/utsname.h>
+#endif
+
+#endif
+
+namespace td {
+
+#if TD_DARWIN || TD_LINUX
+static string read_os_name(CSlice os_version_file_path, CSlice prefix, CSlice suffix) {
+ auto r_stat = stat(os_version_file_path);
+ if (r_stat.is_ok() && r_stat.ok().is_reg_ && r_stat.ok().size_ < (1 << 16)) {
+ auto r_file = read_file_str(os_version_file_path, r_stat.ok().size_);
+ if (r_file.is_ok()) {
+ auto begin_pos = r_file.ok().find(prefix.c_str());
+ if (begin_pos != string::npos) {
+ begin_pos += prefix.size();
+ auto end_pos = r_file.ok().find(suffix.c_str(), begin_pos);
+ if (end_pos != string::npos) {
+ auto os_version = trim(r_file.ok().substr(begin_pos, end_pos - begin_pos));
+ if (os_version.find('\n') == string::npos) {
+ return os_version;
+ }
+ }
+ }
+ }
+ }
+ return string();
+}
+#endif
+
+Slice get_operating_system_version() {
+ static string result = []() -> string {
+#if TD_DARWIN
+ char version[256];
+ size_t size = sizeof(version);
+ string os_version;
+ if (sysctlbyname("kern.osproductversion", version, &size, nullptr, 0) == 0) {
+ os_version = trim(string(version, size));
+ }
+ if (os_version.empty()) {
+ os_version = read_os_name("/System/Library/CoreServices/SystemVersion.plist",
+ "<key>ProductUserVisibleVersion</key>\n\t<string>", "</string>\n");
+ }
+ if (!os_version.empty()) {
+ os_version = " " + os_version;
+ }
+
+#if TD_DARWIN_IOS
+ return "iOS" + os_version;
+#elif TD_DARWIN_TV_OS
+ return "tvOS" + os_version;
+#elif TD_DARWIN_WATCH_OS
+ return "watchOS" + os_version;
+#elif TD_DARWIN_MAC
+ return "macOS" + os_version;
+#else
+ return "Darwin" + os_version;
+#endif
+#elif TD_PORT_POSIX
+#if TD_ANDROID
+ char version[PROP_VALUE_MAX + 1];
+ int length = __system_property_get("ro.build.version.release", version);
+ if (length > 0) {
+ return "Android " + string(version, length);
+ }
+#elif TD_EMSCRIPTEN
+ // clang-format off
+ char *os_name_js = (char*)EM_ASM_INT(({
+ function detectOsName() {
+ if (typeof process === 'object' && typeof process.platform === 'string') { // Node.js
+ switch (process.platform) {
+ case 'aix':
+ return 'IBM AIX';
+ case 'android':
+ return 'Android';
+ case 'darwin':
+ return 'macOS';
+ case 'freebsd':
+ return 'FreeBSD';
+ case 'linux':
+ return 'Linux';
+ case 'openbsd':
+ return 'OpenBSD';
+ case 'sunos':
+ return 'SunOS';
+ case 'win32':
+ return 'Windows';
+ case 'darwin':
+ return 'macOS';
+ default:
+ return 'Node.js';
+ }
+ }
+
+ var userAgent = 'Unknown';
+ if (typeof window === 'object') { // Web
+ userAgent = window.navigator.userAgent;
+ } else if (typeof importScripts === 'function') { // Web Worker
+ userAgent = navigator.userAgent;
+ }
+
+ var match = /(Mac OS|Mac OS X|MacPPC|MacIntel|Mac_PowerPC|Macintosh) ([._0-9]+)/.exec(userAgent);
+ if (match !== null) {
+ return 'macOS ' + match[2].replace('_', '.');
+ }
+
+ match = /Android [._0-9]+/.exec(userAgent);
+ if (match !== null) {
+ return match[0].replace('_', '.');
+ }
+
+ if (/(iPhone|iPad|iPod)/.test(userAgent)) {
+ match = /OS ([._0-9]+)/.exec(userAgent);
+ if (match !== null) {
+ return 'iOS ' + match[1].replace('_', '.');
+ }
+ return 'iOS';
+ }
+
+ var clientStrings = [
+ {s:'Windows 11', r:/(Windows 11|Windows NT 11)/},
+ // there is no way to distinguish Windows 10 from newer versions, so report it as just Windows.
+ // {s:'Windows 10 or later', r:/(Windows 10|Windows NT 10)/},
+ {s:'Windows 8.1', r:/(Windows 8.1|Windows NT 6.3)/},
+ {s:'Windows 8', r:/(Windows 8|Windows NT 6.2)/},
+ {s:'Windows 7', r:/(Windows 7|Windows NT 6.1)/},
+ {s:'Windows Vista', r:/Windows NT 6.0/},
+ {s:'Windows Server 2003', r:/Windows NT 5.2/},
+ {s:'Windows XP', r:/(Windows XP|Windows NT 5.1)/},
+ {s:'Windows', r:/Windows/},
+ {s:'Android', r:/Android/},
+ {s:'FreeBSD', r:/FreeBSD/},
+ {s:'OpenBSD', r:/OpenBSD/},
+ {s:'Chrome OS', r:/CrOS/},
+ {s:'Linux', r:/(Linux|X11)/},
+ {s:'macOS', r:/(Mac OS|MacPPC|MacIntel|Mac_PowerPC|Macintosh)/},
+ {s:'QNX', r:/QNX/},
+ {s:'BeOS', r:/BeOS/}
+ ];
+ for (var id in clientStrings) {
+ var cs = clientStrings[id];
+ if (cs.r.test(userAgent)) {
+ return cs.s;
+ }
+ }
+ return 'Emscripten';
+ }
+
+ var os_name = detectOsName();
+ var length = lengthBytesUTF8(os_name) + 1;
+ var result = _malloc(length);
+ stringToUTF8(os_name, result, length);
+ return result;
+ }));
+ // clang-format on
+ string os_name(os_name_js);
+ std::free(os_name_js);
+
+ return os_name;
+#else
+#if TD_LINUX
+ auto os_name = read_os_name("/etc/os-release", "PRETTY_NAME=\"", "\"\n");
+ if (!os_name.empty()) {
+ return os_name;
+ }
+#endif
+
+ struct utsname name;
+ int err = uname(&name);
+ if (err == 0) {
+ auto os_name = trim(PSTRING() << Slice(name.sysname, std::strlen(name.sysname)) << " "
+ << Slice(name.release, std::strlen(name.release)));
+ if (!os_name.empty()) {
+ return os_name;
+ }
+ }
+#endif
+ LOG(ERROR) << "Failed to identify OS name; use generic one";
+
+#if TD_ANDROID
+ return "Android";
+#elif TD_TIZEN
+ return "Tizen";
+#elif TD_LINUX
+ return "Linux";
+#elif TD_FREEBSD
+ return "FreeBSD";
+#elif TD_OPENBSD
+ return "OpenBSD";
+#elif TD_NETBSD
+ return "NetBSD";
+#elif TD_CYGWIN
+ return "Cygwin";
+#else
+ return "Unix";
+#endif
+
+#else
+
+#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
+ auto handle = GetModuleHandle(L"ntdll.dll");
+ if (handle != nullptr) {
+ using RtlGetVersionPtr = LONG(WINAPI *)(_Out_ PRTL_OSVERSIONINFOEXW);
+ RtlGetVersionPtr RtlGetVersion = reinterpret_cast<RtlGetVersionPtr>(GetProcAddress(handle, "RtlGetVersion"));
+ if (RtlGetVersion != nullptr) {
+ RTL_OSVERSIONINFOEXW os_version_info = {};
+ os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
+ if (RtlGetVersion(&os_version_info) == 0) {
+ auto major = os_version_info.dwMajorVersion;
+ auto minor = os_version_info.dwMinorVersion;
+ bool is_server = os_version_info.wProductType != VER_NT_WORKSTATION;
+
+ if (major == 10) {
+ if (is_server) {
+ if (os_version_info.dwBuildNumber >= 20201) {
+ // https://techcommunity.microsoft.com/t5/windows-server-insiders/announcing/m-p/1614436
+ return "Windows Server 2022";
+ }
+ if (os_version_info.dwBuildNumber >= 17623) {
+ // https://techcommunity.microsoft.com/t5/windows-server-insiders/announcing/m-p/173715
+ return "Windows Server 2019";
+ }
+ return "Windows Server 2016";
+ }
+ if (os_version_info.dwBuildNumber >= 21900) { // build numbers between 21391 and 21999 aren't used
+ return "Windows 11";
+ }
+ return "Windows 10";
+ }
+ if (major == 6 && minor == 3) {
+ return is_server ? "Windows Server 2012 R2" : "Windows 8.1";
+ }
+ if (major == 6 && minor == 2) {
+ return is_server ? "Windows Server 2012" : "Windows 8";
+ }
+ if (major == 6 && minor == 1) {
+ return is_server ? "Windows Server 2008 R2" : "Windows 7";
+ }
+ if (major == 6 && minor == 0) {
+ return is_server ? "Windows Server 2008" : "Windows Vista";
+ }
+ return is_server ? "Windows Server" : "Windows";
+ }
+ }
+ }
+#elif TD_WINRT
+ return "Windows 10";
+#endif
+
+ LOG(ERROR) << "Failed to identify OS name; use generic one";
+ return "Windows";
+#endif
+ }();
+ return result;
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/GitInfo.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/uname.h
index a3ba32602f..aacb0210aa 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/GitInfo.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/uname.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,10 +10,6 @@
namespace td {
-class GitInfo {
- public:
- static CSlice commit();
- static bool is_dirty();
-};
+Slice get_operating_system_version();
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.cpp
new file mode 100644
index 0000000000..b793405b82
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.cpp
@@ -0,0 +1,57 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/user.h"
+
+#include "td/utils/port/config.h"
+
+#if TD_PORT_POSIX
+#include "td/utils/SliceBuilder.h"
+
+#include <grp.h>
+#include <pwd.h>
+#if TD_DARWIN || TD_FREEBSD || TD_NETBSD
+#include <sys/param.h>
+#endif
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+namespace td {
+
+Status change_user(CSlice username, CSlice groupname) {
+#if TD_PORT_POSIX
+ passwd *pw = getpwnam(username.c_str());
+ if (pw == nullptr) {
+ return OS_ERROR(PSTRING() << "Can't find the user '" << username << "' to switch to");
+ }
+ uid_t uid = pw->pw_uid;
+ gid_t gid = pw->pw_gid;
+ if (setgroups(1, &gid) == -1) {
+ return OS_ERROR("Failed to clear supplementary group list");
+ }
+ if (!groupname.empty()) {
+ group *g = getgrnam(groupname.c_str());
+ if (g == nullptr) {
+ return OS_ERROR("Can't find the group to switch to");
+ }
+ gid = g->gr_gid;
+ } else if (initgroups(username.c_str(), gid) == -1) {
+ return OS_ERROR("Failed to load groups of user");
+ }
+ if (setgid(gid) == -1) {
+ return OS_ERROR("failed to set effective group ID");
+ }
+ if (setuid(uid) == -1) {
+ return OS_ERROR("failed to set effective user ID");
+ }
+ return Status::OK();
+#else
+ return Status::Error("Changing effective user is not supported");
+#endif
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.h
new file mode 100644
index 0000000000..041df03e83
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/user.h
@@ -0,0 +1,16 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+
+namespace td {
+
+Status change_user(CSlice username, CSlice groupname = CSlice());
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.cpp
index 56da62b96d..fd6f4dfa23 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,44 +10,95 @@ char disable_linker_warning_about_empty_file_wstring_convert_cpp TD_UNUSED;
#if TD_PORT_WINDOWS
-#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
+#include "td/utils/base64.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/utf8.h"
-#include "td/utils/port/wstring_convert.h"
-
-#include <codecvt>
-#include <locale>
-#include <utility>
+#include <cwchar>
namespace td {
-namespace detail {
-template <class Facet>
-class UsableFacet : public Facet {
- public:
- template <class... Args>
- explicit UsableFacet(Args &&... args) : Facet(std::forward<Args>(args)...) {
+Result<std::wstring> to_wstring(CSlice slice) {
+ if (!check_utf8(slice)) {
+ return Status::Error(PSLICE() << "String was expected to be encoded in UTF-8: " << base64_encode(slice));
}
- ~UsableFacet() = default;
-};
-} // namespace detail
-
-Result<std::wstring> to_wstring(Slice slice) {
- // TODO(perf): optimize
- std::wstring_convert<detail::UsableFacet<std::codecvt_utf8_utf16<wchar_t>>> converter;
- auto res = converter.from_bytes(slice.begin(), slice.end());
- if (converter.converted() != slice.size()) {
- return Status::Error("Wrong encoding");
+
+ size_t wstring_len = utf8_utf16_length(slice);
+
+ std::wstring result(wstring_len, static_cast<wchar_t>(0));
+ if (wstring_len) {
+ wchar_t *res = &result[0];
+ for (size_t i = 0; i < slice.size();) {
+ uint32 a = static_cast<unsigned char>(slice[i++]);
+ if (a >= 0x80) {
+ uint32 b = static_cast<unsigned char>(slice[i++]);
+ if (a >= 0xe0) {
+ uint32 c = static_cast<unsigned char>(slice[i++]);
+ if (a >= 0xf0) {
+ uint32 d = static_cast<unsigned char>(slice[i++]);
+ uint32 val = ((a & 0x07) << 18) + ((b & 0x3f) << 12) + ((c & 0x3f) << 6) + (d & 0x3f) - 0x10000;
+ *res++ = static_cast<wchar_t>(0xD800 + (val >> 10));
+ *res++ = static_cast<wchar_t>(0xDC00 + (val & 0x3ff));
+ } else {
+ *res++ = static_cast<wchar_t>(((a & 0x0f) << 12) + ((b & 0x3f) << 6) + (c & 0x3f));
+ }
+ } else {
+ *res++ = static_cast<wchar_t>(((a & 0x1f) << 6) + (b & 0x3f));
+ }
+ } else {
+ *res++ = static_cast<wchar_t>(a);
+ }
+ }
+ CHECK(res == &result[0] + wstring_len);
}
- return res;
+ return result;
}
Result<string> from_wstring(const wchar_t *begin, size_t size) {
- std::wstring_convert<detail::UsableFacet<std::codecvt_utf8_utf16<wchar_t>>> converter;
- auto res = converter.to_bytes(begin, begin + size);
- if (converter.converted() != size) {
- return Status::Error("Wrong encoding");
+ size_t result_len = 0;
+ for (size_t i = 0; i < size; i++) {
+ uint32 cur = begin[i];
+ if ((cur & 0xF800) == 0xD800) {
+ if (i < size) {
+ uint32 next = begin[++i];
+ if ((next & 0xFC00) == 0xDC00 && (cur & 0x400) == 0) {
+ result_len += 4;
+ continue;
+ }
+ }
+
+ return Status::Error("Wrong wstring encoding");
+ }
+ result_len += 1 + (cur >= 0x80) + (cur >= 0x800);
+ }
+
+ std::string result(result_len, '\0');
+ if (result_len) {
+ char *res = &result[0];
+ for (size_t i = 0; i < size; i++) {
+ uint32 cur = begin[i];
+ // TODO conversion uint32 -> signed char is implementation defined
+ if (cur <= 0x7f) {
+ *res++ = static_cast<char>(cur);
+ } else if (cur <= 0x7ff) {
+ *res++ = static_cast<char>(0xc0 | (cur >> 6));
+ *res++ = static_cast<char>(0x80 | (cur & 0x3f));
+ } else if ((cur & 0xF800) != 0xD800) {
+ *res++ = static_cast<char>(0xe0 | (cur >> 12));
+ *res++ = static_cast<char>(0x80 | ((cur >> 6) & 0x3f));
+ *res++ = static_cast<char>(0x80 | (cur & 0x3f));
+ } else {
+ uint32 next = begin[++i];
+ uint32 val = ((cur - 0xD800) << 10) + next - 0xDC00 + 0x10000;
+
+ *res++ = static_cast<char>(0xf0 | (val >> 18));
+ *res++ = static_cast<char>(0x80 | ((val >> 12) & 0x3f));
+ *res++ = static_cast<char>(0x80 | ((val >> 6) & 0x3f));
+ *res++ = static_cast<char>(0x80 | (val & 0x3f));
+ }
+ }
}
- return res;
+ return result;
}
Result<string> from_wstring(const std::wstring &str) {
@@ -55,7 +106,7 @@ Result<string> from_wstring(const std::wstring &str) {
}
Result<string> from_wstring(const wchar_t *begin) {
- return from_wstring(begin, wcslen(begin));
+ return from_wstring(begin, std::wcslen(begin));
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.h
index a795d2bd92..fe453380d3 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/port/wstring_convert.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -18,7 +18,7 @@
namespace td {
-Result<std::wstring> to_wstring(Slice slice);
+Result<std::wstring> to_wstring(CSlice slice);
Result<string> from_wstring(const std::wstring &str);
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/queue.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/queue.h
index 6d107e37f2..e6d69aae3e 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/queue.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/queue.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,15 +7,10 @@
#pragma once
#include "td/utils/port/EventFd.h"
-#include "td/utils/port/thread.h"
+#include "td/utils/port/sleep.h"
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
-#if !TD_WINDOWS
-#include <poll.h>
-#include <sched.h>
-#endif
-
#include <atomic>
#include <type_traits>
#include <utility>
@@ -37,7 +32,7 @@ class Backoff {
if (cnt < 1) { // 50
return true;
} else {
- td::this_thread::yield();
+ usleep_for(1);
return cnt < 3; // 500
}
}
@@ -52,7 +47,7 @@ class InfBackoff {
if (cnt < 50) {
return true;
} else {
- td::this_thread::yield();
+ usleep_for(1);
return true;
}
}
@@ -72,7 +67,7 @@ class SPSCBlockQueue {
}
struct Position {
- std::atomic<uint32> i;
+ std::atomic<uint32> i{0};
char pad[64 - sizeof(std::atomic<uint32>)];
uint32 local_writer_i;
char pad2[64 - sizeof(uint32)];
@@ -155,7 +150,7 @@ class SPSCBlockQueue {
}
};
-template <class T, class BlockQueueT = SPSCBlockQueue<T> >
+template <class T, class BlockQueueT = SPSCBlockQueue<T>>
class SPSCChainQueue {
public:
using ValueType = T;
@@ -257,7 +252,7 @@ class SPSCChainQueue {
private:
struct Node {
BlockQueueT q_;
- std::atomic<bool> is_closed_;
+ std::atomic<bool> is_closed_{false};
Node *next_;
void init() {
@@ -313,11 +308,11 @@ class BackoffQueue : public QueueT {
}
};
-template <class T, class QueueT = SPSCChainQueue<T> >
+template <class T, class QueueT = SPSCChainQueue<T>>
using InfBackoffQueue = BackoffQueue<T, QueueT, detail::InfBackoff>;
-template <class T, class QueueT = BackoffQueue<T> >
-class PollQueue : public QueueT {
+template <class T, class QueueT = BackoffQueue<T>>
+class PollQueue final : public QueueT {
public:
using ValueType = T;
using QueueType = QueueT;
@@ -393,25 +388,18 @@ class PollQueue : public QueueT {
return res;
}
-// Just example of usage
-#if !TD_WINDOWS
+ // Just an example of usage
int reader_wait() {
int res;
-
while ((res = reader_wait_nonblock()) == 0) {
- // TODO: reader_flush?
- pollfd fd;
- fd.fd = reader_get_event_fd().get_fd().get_native_fd();
- fd.events = POLLIN;
- poll(&fd, 1, -1);
+ reader_get_event_fd().wait(1000);
}
return res;
}
-#endif
private:
EventFd event_fd_;
- std::atomic<int> wait_state_;
+ std::atomic<int> wait_state_{0};
int writer_wait_state_;
int get_wait_state() {
@@ -433,7 +421,7 @@ class PollQueue : public QueueT {
#else
-#include "td/utils/logging.h"
+#include "td/utils/common.h"
namespace td {
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/tests.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/tests.cpp
new file mode 100644
index 0000000000..54fda579ed
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/tests.cpp
@@ -0,0 +1,271 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/tests.h"
+
+#include "td/utils/crypto.h"
+#include "td/utils/filesystem.h"
+#include "td/utils/Parser.h"
+#include "td/utils/PathView.h"
+#include "td/utils/port/path.h"
+#include "td/utils/port/Stat.h"
+#include "td/utils/Random.h"
+#include "td/utils/ScopeGuard.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/StringBuilder.h"
+#include "td/utils/Time.h"
+
+#include <map>
+
+namespace td {
+
+string rand_string(int from, int to, size_t len) {
+ string res(len, '\0');
+ for (auto &c : res) {
+ c = static_cast<char>(Random::fast(from, to));
+ }
+ return res;
+}
+
+vector<string> rand_split(Slice str) {
+ vector<string> res;
+ size_t pos = 0;
+ while (pos < str.size()) {
+ size_t len;
+ if (Random::fast_bool()) {
+ len = Random::fast(1, 10);
+ } else {
+ len = Random::fast(100, 200);
+ }
+ res.push_back(str.substr(pos, len).str());
+ pos += len;
+ }
+ return res;
+}
+
+struct TestInfo {
+ string name;
+ string result_hash; // base64
+};
+
+StringBuilder &operator<<(StringBuilder &sb, const TestInfo &info) {
+ // should I use JSON?
+ CHECK(!info.name.empty());
+ CHECK(!info.result_hash.empty());
+ return sb << info.name << " " << info.result_hash << "\n";
+}
+
+class RegressionTesterImpl final : public RegressionTester {
+ public:
+ static void destroy(CSlice db_path) {
+ unlink(db_path).ignore();
+ }
+
+ RegressionTesterImpl(string db_path, string db_cache_dir)
+ : db_path_(std::move(db_path)), db_cache_dir_(std::move(db_cache_dir)) {
+ load_db(db_path_).ignore();
+ if (db_cache_dir_.empty()) {
+ db_cache_dir_ = PathView(db_path_).without_extension().str() + ".cache/";
+ }
+ mkdir(db_cache_dir_).ensure();
+ }
+
+ Status verify_test(Slice name, Slice result) final {
+#if TD_HAVE_OPENSSL
+ auto hash = PSTRING() << format::as_hex_dump<0>(Slice(sha256(result)));
+#else
+ auto hash = to_string(crc64(result));
+#endif
+ TestInfo &old_test_info = tests_[name.str()];
+ if (!old_test_info.result_hash.empty() && old_test_info.result_hash != hash) {
+ auto wa_path = db_cache_dir_ + "WA";
+ write_file(wa_path, result).ensure();
+ return Status::Error(PSLICE() << "Test " << name << " changed: " << tag("expected", old_test_info.result_hash)
+ << tag("got", hash));
+ }
+ auto result_cache_path = db_cache_dir_ + hash;
+ if (stat(result_cache_path).is_error()) {
+ write_file(result_cache_path, result).ensure();
+ }
+ if (!old_test_info.result_hash.empty()) {
+ return Status::OK();
+ }
+ old_test_info.name = name.str();
+ old_test_info.result_hash = hash;
+ is_dirty_ = true;
+
+ return Status::OK();
+ }
+
+ void save_db() final {
+ if (!is_dirty_) {
+ return;
+ }
+ SCOPE_EXIT {
+ is_dirty_ = false;
+ };
+ string buf(2000000, ' ');
+ StringBuilder sb(buf);
+ save_db(sb);
+ string new_db_path = db_path_ + ".new";
+ write_file(new_db_path, sb.as_cslice()).ensure();
+ rename(new_db_path, db_path_).ensure();
+ }
+
+ Slice magic() const {
+ return Slice("abce");
+ }
+
+ void save_db(StringBuilder &sb) {
+ sb << magic() << "\n";
+ for (const auto &it : tests_) {
+ sb << it.second;
+ }
+ }
+
+ Status load_db(CSlice path) {
+ TRY_RESULT(data, read_file(path));
+ ConstParser parser(data.as_slice());
+ auto db_magic = parser.read_word();
+ if (db_magic != magic()) {
+ return Status::Error(PSLICE() << "Wrong magic " << db_magic);
+ }
+ while (true) {
+ TestInfo info;
+ info.name = parser.read_word().str();
+ if (info.name.empty()) {
+ break;
+ }
+ info.result_hash = parser.read_word().str();
+ tests_[info.name] = info;
+ }
+ return Status::OK();
+ }
+
+ private:
+ string db_path_;
+ string db_cache_dir_;
+ bool is_dirty_{false};
+
+ std::map<string, TestInfo> tests_;
+};
+
+void RegressionTester::destroy(CSlice path) {
+ RegressionTesterImpl::destroy(path);
+}
+
+unique_ptr<RegressionTester> RegressionTester::create(string db_path, string db_cache_dir) {
+ return td::make_unique<RegressionTesterImpl>(std::move(db_path), std::move(db_cache_dir));
+}
+
+TestsRunner &TestsRunner::get_default() {
+ static TestsRunner default_runner;
+ return default_runner;
+}
+
+void TestsRunner::add_test(string name, std::function<unique_ptr<Test>()> test) {
+ for (auto &it : tests_) {
+ if (it.first == name) {
+ LOG(FATAL) << "Test name collision " << name;
+ }
+ }
+ tests_.emplace_back(std::move(name), TestInfo{std::move(test), nullptr});
+}
+
+void TestsRunner::add_substr_filter(string str) {
+ if (str[0] != '+' && str[0] != '-') {
+ str = "+" + str;
+ }
+ substr_filters_.push_back(std::move(str));
+}
+
+void TestsRunner::set_regression_tester(unique_ptr<RegressionTester> regression_tester) {
+ regression_tester_ = std::move(regression_tester);
+}
+
+void TestsRunner::set_stress_flag(bool flag) {
+ stress_flag_ = flag;
+}
+
+void TestsRunner::run_all() {
+ while (run_all_step()) {
+ }
+}
+
+bool TestsRunner::run_all_step() {
+ Guard guard(this);
+ if (state_.it == state_.end) {
+ state_.end = tests_.size();
+ state_.it = 0;
+ }
+
+ while (state_.it != state_.end) {
+ auto &name = tests_[state_.it].first;
+ auto &test = tests_[state_.it].second.test;
+ if (!state_.is_running) {
+ bool ok = true;
+ for (const auto &filter : substr_filters_) {
+ bool is_match = name.find(filter.substr(1)) != string::npos;
+ if (is_match != (filter[0] == '+')) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) {
+ ++state_.it;
+ continue;
+ }
+ LOG(ERROR) << "Run test " << tag("name", name);
+ state_.start = Time::now();
+ state_.start_unadjusted = Time::now_unadjusted();
+ state_.is_running = true;
+
+ CHECK(!test);
+ test = tests_[state_.it].second.creator();
+ }
+
+ if (test->step()) {
+ break;
+ }
+
+ test = {};
+
+ auto passed = Time::now() - state_.start;
+ auto real_passed = Time::now_unadjusted() - state_.start_unadjusted;
+ if (real_passed + 1e-1 > passed) {
+ LOG(ERROR) << format::as_time(passed);
+ } else {
+ LOG(ERROR) << format::as_time(real_passed) << " adjusted [" << format::as_time(real_passed) << "]";
+ }
+ if (regression_tester_) {
+ regression_tester_->save_db();
+ }
+ state_.is_running = false;
+ ++state_.it;
+ }
+
+ auto ret = state_.it != state_.end;
+ if (!ret) {
+ state_ = State();
+ }
+ return ret || stress_flag_;
+}
+
+Slice TestsRunner::name() {
+ CHECK(state_.is_running);
+ return tests_[state_.it].first;
+}
+
+Status TestsRunner::verify(Slice data) {
+ if (!regression_tester_) {
+ LOG(INFO) << data;
+ LOG(ERROR) << "Cannot verify and save <" << name() << "> answer. Use --regression <regression_db> option";
+ return Status::OK();
+ }
+ return regression_tester_->verify_test(PSLICE() << name() << "_default", data);
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/tests.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/tests.h
index 24e2f3fe22..69b9206268 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/tests.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/tests.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,139 +7,133 @@
#pragma once
#include "td/utils/common.h"
+#include "td/utils/Context.h"
#include "td/utils/format.h"
-#include "td/utils/List.h"
#include "td/utils/logging.h"
-#include "td/utils/port/thread.h"
-#include "td/utils/Random.h"
+#include "td/utils/port/sleep.h"
#include "td/utils/Slice.h"
-#include "td/utils/Time.h"
+#include "td/utils/Status.h"
#include <atomic>
-
-#define REGISTER_TESTS(x) \
- void TD_CONCAT(register_tests_, x)() { \
- }
-#define DESC_TESTS(x) void TD_CONCAT(register_tests_, x)()
-#define LOAD_TESTS(x) TD_CONCAT(register_tests_, x)()
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <utility>
namespace td {
-class Test : private ListNode {
+class RandomSteps {
public:
- explicit Test(CSlice name) : name_(name) {
- get_tests_list()->put_back(this);
- }
-
- Test(const Test &) = delete;
- Test &operator=(const Test &) = delete;
- Test(Test &&) = delete;
- Test &operator=(Test &&) = delete;
- virtual ~Test() = default;
+ struct Step {
+ std::function<void()> func;
+ uint32 weight;
+ };
- static void add_substr_filter(std::string str) {
- if (str[0] != '+' && str[0] != '-') {
- str = "+" + str;
+ explicit RandomSteps(vector<Step> steps) : steps_(std::move(steps)) {
+ for (const auto &step : steps_) {
+ steps_sum_ += step.weight;
}
- get_substr_filters()->push_back(std::move(str));
}
- static void set_stress_flag(bool flag) {
- get_stress_flag() = flag;
- }
- static void run_all() {
- while (run_all_step()) {
- }
- }
-
- static bool run_all_step() {
- auto *state = get_state();
- if (state->it == nullptr) {
- state->end = get_tests_list();
- state->it = state->end->next;
- }
- while (state->it != state->end) {
- auto test = static_cast<td::Test *>(state->it);
- if (!state->is_running) {
- bool ok = true;
- for (const auto &filter : *get_substr_filters()) {
- bool is_match = test->name_.str().find(filter.substr(1)) != std::string::npos;
- if (is_match != (filter[0] == '+')) {
- ok = false;
- break;
- }
- }
- if (!ok) {
- state->it = state->it->next;
- continue;
- }
- LOG(ERROR) << "Run test " << tag("name", test->name_);
- state->start = Time::now();
- state->is_running = true;
- }
-
- if (test->step()) {
+ template <class Random>
+ void step(Random &rnd) const {
+ auto w = rnd() % steps_sum_;
+ for (const auto &step : steps_) {
+ if (w < step.weight) {
+ step.func();
break;
}
-
- LOG(ERROR) << format::as_time(Time::now() - state->start);
- state->is_running = false;
- state->it = state->it->next;
+ w -= step.weight;
}
-
- auto ret = state->it != state->end;
- if (!ret) {
- *state = State();
- }
- return ret || get_stress_flag();
}
private:
- CSlice name_;
- struct State {
- ListNode *it = nullptr;
- bool is_running = false;
- double start;
- ListNode *end = nullptr;
- };
- static State *get_state() {
- static State state;
- return &state;
- }
- static std::vector<std::string> *get_substr_filters() {
- static std::vector<std::string> substr_filters_;
- return &substr_filters_;
- }
+ vector<Step> steps_;
+ int32 steps_sum_ = 0;
+};
- static ListNode *get_tests_list() {
- static ListNode root;
- return &root;
- }
- static bool &get_ok_flag() {
- static bool is_ok = true;
- return is_ok;
- }
- static bool &get_stress_flag() {
- static bool stress_flag = false;
- return stress_flag;
- }
+class RegressionTester {
+ public:
+ virtual ~RegressionTester() = default;
+ static void destroy(CSlice db_path);
+ static unique_ptr<RegressionTester> create(string db_path, string db_cache_dir = "");
+
+ virtual Status verify_test(Slice name, Slice result) = 0;
+ virtual void save_db() = 0;
+};
+
+class Test {
+ public:
+ virtual ~Test() = default;
virtual void run() {
while (step()) {
}
}
-
virtual bool step() {
run();
return false;
}
+ Test() = default;
+ Test(const Test &) = delete;
+ Test &operator=(const Test &) = delete;
+ Test(Test &&) = delete;
+ Test &operator=(Test &&) = delete;
+};
+
+class TestContext : public Context<TestContext> {
+ public:
+ virtual ~TestContext() = default;
+ virtual Slice name() = 0;
+ virtual Status verify(Slice data) = 0;
+};
+
+class TestsRunner final : public TestContext {
+ public:
+ static TestsRunner &get_default();
+
+ void add_test(string name, std::function<unique_ptr<Test>()> test);
+ void add_substr_filter(string str);
+ void set_stress_flag(bool flag);
+ void run_all();
+ bool run_all_step();
+ void set_regression_tester(unique_ptr<RegressionTester> regression_tester);
+
+ private:
+ struct State {
+ size_t it{0};
+ bool is_running = false;
+ double start{0};
+ double start_unadjusted{0};
+ size_t end{0};
+ };
+ bool stress_flag_{false};
+ vector<string> substr_filters_;
+ struct TestInfo {
+ std::function<unique_ptr<Test>()> creator;
+ unique_ptr<Test> test;
+ };
+ vector<std::pair<string, TestInfo>> tests_;
+ State state_;
+ unique_ptr<RegressionTester> regression_tester_;
+
+ Slice name() final;
+ Status verify(Slice data) final;
+};
+
+template <class T>
+class RegisterTest {
+ public:
+ explicit RegisterTest(string name, TestsRunner &runner = TestsRunner::get_default()) {
+ runner.add_test(std::move(name), [] { return make_unique<T>(); });
+ }
};
-class Stage {
+class StageWait {
public:
void wait(uint64 need) {
value_.fetch_add(1, std::memory_order_release);
while (value_.load(std::memory_order_acquire) < need) {
- td::this_thread::yield();
+ usleep_for(1);
}
};
@@ -147,38 +141,38 @@ class Stage {
std::atomic<uint64> value_{0};
};
-inline string rand_string(char from, char to, int len) {
- string res(len, 0);
- for (auto &c : res) {
- c = static_cast<char>(Random::fast(from, to));
- }
- return res;
-}
-
-inline std::vector<string> rand_split(string str) {
- std::vector<string> res;
- size_t pos = 0;
- while (pos < str.size()) {
- size_t len;
- if (Random::fast(0, 1) == 1) {
- len = Random::fast(1, 10);
- } else {
- len = Random::fast(100, 200);
+class StageMutex {
+ public:
+ void wait(uint64 need) {
+ std::unique_lock<std::mutex> lock{mutex_};
+ value_++;
+ if (value_ == need) {
+ cond_.notify_all();
+ return;
}
- res.push_back(str.substr(pos, len));
- pos += len;
- }
- return res;
-}
+ cond_.wait(lock, [&] { return value_ >= need; });
+ };
+
+ private:
+ std::mutex mutex_;
+ std::condition_variable cond_;
+ uint64 value_{0};
+};
+
+using Stage = StageMutex;
+
+string rand_string(int from, int to, size_t len);
+
+vector<string> rand_split(Slice str);
template <class T1, class T2>
void assert_eq_impl(const T1 &expected, const T2 &got, const char *file, int line) {
- CHECK(expected == got) << tag("expected", expected) << tag("got", got) << " in " << file << " at line " << line;
+ LOG_CHECK(expected == got) << tag("expected", expected) << tag("got", got) << " in " << file << " at line " << line;
}
template <class T>
void assert_true_impl(const T &got, const char *file, int line) {
- CHECK(got) << "Expected true in " << file << " at line " << line;
+ LOG_CHECK(got) << "Expected true in " << file << " at line " << line;
}
} // namespace td
@@ -190,16 +184,18 @@ void assert_true_impl(const T &got, const char *file, int line) {
#define ASSERT_STREQ(expected, got) \
::td::assert_eq_impl(::td::Slice((expected)), ::td::Slice((got)), __FILE__, __LINE__)
+#define REGRESSION_VERIFY(data) ::td::TestContext::get()->verify(data).ensure()
+
#define TEST_NAME(test_case_name, test_name) \
TD_CONCAT(Test, TD_CONCAT(_, TD_CONCAT(test_case_name, TD_CONCAT(_, test_name))))
#define TEST(test_case_name, test_name) TEST_IMPL(TEST_NAME(test_case_name, test_name))
-#define TEST_IMPL(test_name) \
- class test_name : public ::td::Test { \
- public: \
- using Test::Test; \
- void run() final; \
- }; \
- test_name TD_CONCAT(test_instance_, TD_CONCAT(test_name, __LINE__))(TD_DEFINE_STR(test_name)); \
+#define TEST_IMPL(test_name) \
+ class test_name final : public ::td::Test { \
+ public: \
+ using Test::Test; \
+ void run() final; \
+ }; \
+ ::td::RegisterTest<test_name> TD_CONCAT(test_instance_, TD_CONCAT(test_name, __LINE__))(TD_DEFINE_STR(test_name)); \
void test_name::run()
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_helpers.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_helpers.h
index 686dacbeef..ee5c9e6c3e 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_helpers.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_helpers.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -7,43 +7,57 @@
#pragma once
#include "td/utils/common.h"
-#include "td/utils/logging.h"
+#include "td/utils/FlatHashSet.h"
#include "td/utils/misc.h"
+#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/StackAllocator.h"
#include "td/utils/Status.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/tl_storers.h"
+#include "td/utils/Variant.h"
#include <type_traits>
-#include <unordered_set>
+#include <utility>
-#define BEGIN_STORE_FLAGS() \
- uint32 flags_store = 0; \
- uint32 bit_offset_store = 0
+#define BEGIN_STORE_FLAGS() \
+ do { \
+ ::td::uint32 flags_store = 0; \
+ ::td::uint32 bit_offset_store = 0
-#define STORE_FLAG(flag) \
- flags_store |= (flag) << bit_offset_store; \
+#define STORE_FLAG(flag) \
+ static_assert(std::is_same<decltype(flag), bool>::value, "flag must be bool"); \
+ flags_store |= (flag) << bit_offset_store; \
bit_offset_store++
-#define END_STORE_FLAGS() \
- CHECK(bit_offset_store < 31); \
- td::store(flags_store, storer)
+#define END_STORE_FLAGS() \
+ CHECK(bit_offset_store < 31); \
+ ::td::store(flags_store, storer); \
+ } \
+ while (false)
-#define BEGIN_PARSE_FLAGS() \
- uint32 flags_parse; \
- uint32 bit_offset_parse = 0; \
- td::parse(flags_parse, parser)
+#define BEGIN_PARSE_FLAGS() \
+ do { \
+ ::td::uint32 flags_parse; \
+ ::td::uint32 bit_offset_parse = 0; \
+ ::td::parse(flags_parse, parser)
-#define PARSE_FLAG(flag) \
- flag = ((flags_parse >> bit_offset_parse) & 1) != 0; \
+#define PARSE_FLAG(flag) \
+ static_assert(std::is_same<decltype(flag), bool>::value, "flag must be bool"); \
+ flag = ((flags_parse >> bit_offset_parse) & 1) != 0; \
bit_offset_parse++
-#define END_PARSE_FLAGS() \
- CHECK(bit_offset_parse < 31); \
- CHECK((flags_parse & ~((1 << bit_offset_parse) - 1)) == 0) << flags_parse << " " << bit_offset_parse;
+#define END_PARSE_FLAGS() \
+ CHECK(bit_offset_parse < 31); \
+ if ((flags_parse & ~((1 << bit_offset_parse) - 1)) != 0) { \
+ parser.set_error(PSTRING() << "Invalid flags " << flags_parse << " left, current bit is " << bit_offset_parse); \
+ } \
+ } \
+ while (false)
namespace td {
+
template <class StorerT>
void store(bool x, StorerT &storer) {
storer.store_binary(static_cast<int32>(x));
@@ -105,11 +119,20 @@ template <class StorerT>
void store(const string &x, StorerT &storer) {
storer.store_string(x);
}
+template <class StorerT>
+void store(const SecureString &x, StorerT &storer) {
+ storer.store_string(x.as_slice());
+}
template <class ParserT>
void parse(string &x, ParserT &parser) {
x = parser.template fetch_string<string>();
}
+template <class ParserT>
+void parse(SecureString &x, ParserT &parser) {
+ x = parser.template fetch_string<SecureString>();
+}
+
template <class T, class StorerT>
void store(const vector<T> &vec, StorerT &storer) {
storer.store_binary(narrow_cast<int32>(vec.size()));
@@ -117,6 +140,13 @@ void store(const vector<T> &vec, StorerT &storer) {
store(val, storer);
}
}
+template <class T, class StorerT>
+void store(const vector<T *> &vec, StorerT &storer) {
+ storer.store_binary(narrow_cast<int32>(vec.size()));
+ for (auto &val : vec) {
+ store(*val, storer);
+ }
+}
template <class T, class ParserT>
void parse(vector<T> &vec, ParserT &parser) {
uint32 size = parser.fetch_int();
@@ -130,28 +160,51 @@ void parse(vector<T> &vec, ParserT &parser) {
}
}
-template <class Key, class Hash, class KeyEqual, class Allocator, class StorerT>
-void store(const std::unordered_set<Key, Hash, KeyEqual, Allocator> &s, StorerT &storer) {
+template <class T, class StorerT>
+void store(const unique_ptr<T> &ptr, StorerT &storer) {
+ CHECK(ptr != nullptr);
+ store(*ptr, storer);
+}
+template <class T, class ParserT>
+void parse(unique_ptr<T> &ptr, ParserT &parser) {
+ CHECK(ptr == nullptr);
+ ptr = make_unique<T>();
+ parse(*ptr, parser);
+}
+
+template <class Key, class Hash, class KeyEqual, class StorerT>
+void store(const FlatHashSet<Key, Hash, KeyEqual> &s, StorerT &storer) {
storer.store_binary(narrow_cast<int32>(s.size()));
for (auto &val : s) {
store(val, storer);
}
}
-template <class Key, class Hash, class KeyEqual, class Allocator, class ParserT>
-void parse(std::unordered_set<Key, Hash, KeyEqual, Allocator> &s, ParserT &parser) {
+template <class Key, class Hash, class KeyEqual, class ParserT>
+void parse(FlatHashSet<Key, Hash, KeyEqual> &s, ParserT &parser) {
uint32 size = parser.fetch_int();
if (parser.get_left_len() < size) {
parser.set_error("Wrong set length");
return;
}
s.clear();
- Key val;
for (uint32 i = 0; i < size; i++) {
+ Key val;
parse(val, parser);
s.insert(std::move(val));
}
}
+template <class U, class V, class StorerT>
+void store(const std::pair<U, V> &pair, StorerT &storer) {
+ store(pair.first, storer);
+ store(pair.second, storer);
+}
+template <class U, class V, class ParserT>
+void parse(std::pair<U, V> &pair, ParserT &parser) {
+ parse(pair.first, parser);
+ parse(pair.second, parser);
+}
+
template <class T, class StorerT>
std::enable_if_t<std::is_enum<T>::value> store(const T &val, StorerT &storer) {
store(static_cast<int32>(val), storer);
@@ -172,6 +225,29 @@ std::enable_if_t<!std::is_enum<T>::value> parse(T &val, ParserT &parser) {
val.parse(parser);
}
+template <class... Types, class StorerT>
+void store(const Variant<Types...> &variant, StorerT &storer) {
+ store(variant.get_offset(), storer);
+ variant.visit([&storer](auto &&value) {
+ using td::store;
+ store(value, storer);
+ });
+}
+template <class... Types, class ParserT>
+void parse(Variant<Types...> &variant, ParserT &parser) {
+ auto type_offset = parser.fetch_int();
+ if (type_offset < 0 || type_offset >= static_cast<int32>(sizeof...(Types))) {
+ return parser.set_error("Invalid type");
+ }
+ variant.for_each([type_offset, &parser, &variant](int offset, auto *ptr) {
+ using T = std::decay_t<decltype(*ptr)>;
+ if (offset == type_offset) {
+ variant = T();
+ parse(variant.template get<T>(), parser);
+ }
+ });
+}
+
template <class T>
string serialize(const T &object) {
TlStorerCalcLength calc_length;
@@ -182,22 +258,40 @@ string serialize(const T &object) {
if (!is_aligned_pointer<4>(key.data())) {
auto ptr = StackAllocator::alloc(length);
MutableSlice data = ptr.as_slice();
- TlStorerUnsafe storer(data.begin());
+ TlStorerUnsafe storer(data.ubegin());
store(object, storer);
+ CHECK(storer.get_buf() == data.uend());
key.assign(data.begin(), data.size());
} else {
MutableSlice data = key;
- TlStorerUnsafe storer(data.begin());
+ TlStorerUnsafe storer(data.ubegin());
store(object, storer);
+ CHECK(storer.get_buf() == data.uend());
}
return key;
}
template <class T>
+SecureString serialize_secure(const T &object) {
+ TlStorerCalcLength calc_length;
+ store(object, calc_length);
+ size_t length = calc_length.get_length();
+
+ SecureString key(length, '\0');
+ CHECK(is_aligned_pointer<4>(key.data()));
+ MutableSlice data = key.as_mutable_slice();
+ TlStorerUnsafe storer(data.ubegin());
+ store(object, storer);
+ CHECK(storer.get_buf() == data.uend());
+ return key;
+}
+
+template <class T>
TD_WARN_UNUSED_RESULT Status unserialize(T &object, Slice data) {
TlParser parser(data);
parse(object, parser);
parser.fetch_end();
return parser.get_status();
}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.cpp
index 534e7793cf..d4315a43de 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.cpp
@@ -1,15 +1,35 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/tl_parsers.h"
+#include "td/utils/misc.h"
+
namespace td {
alignas(4) const unsigned char TlParser::empty_data[sizeof(UInt256)] = {}; // static zero-initialized
+TlParser::TlParser(Slice slice) {
+ data_len = left_len = slice.size();
+ if (is_aligned_pointer<4>(slice.begin())) {
+ data = slice.ubegin();
+ } else {
+ int32 *buf;
+ if (data_len <= small_data_array.size() * sizeof(int32)) {
+ buf = &small_data_array[0];
+ } else {
+ LOG(ERROR) << "Unexpected big unaligned data pointer of length " << slice.size() << " at " << slice.begin();
+ data_buf = std::make_unique<int32[]>(1 + data_len / sizeof(int32));
+ buf = data_buf.get();
+ }
+ std::memcpy(buf, slice.begin(), slice.size());
+ data = reinterpret_cast<unsigned char *>(buf);
+ }
+}
+
void TlParser::set_error(const string &error_message) {
if (error.empty()) {
CHECK(!error_message.empty());
@@ -19,11 +39,21 @@ void TlParser::set_error(const string &error_message) {
left_len = 0;
data_len = 0;
} else {
+ LOG_CHECK(error_pos != std::numeric_limits<size_t>::max() && data_len == 0 && left_len == 0)
+ << data_len << " " << left_len << " " << data << " " << &empty_data[0] << " " << error_pos << " " << error
+ << " " << data << " " << &empty_data;
data = empty_data;
- CHECK(error_pos != std::numeric_limits<size_t>::max());
- CHECK(data_len == 0);
- CHECK(left_len == 0);
}
}
+BufferSlice TlBufferParser::as_buffer_slice(Slice slice) {
+ if (slice.empty()) {
+ return BufferSlice();
+ }
+ if (is_aligned_pointer<4>(slice.data())) {
+ return parent_->from_slice(slice);
+ }
+ return BufferSlice(slice);
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.h
index ffb669bdeb..da9f2144e4 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_parsers.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -10,14 +10,16 @@
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
-#include "td/utils/misc.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/Status.h"
+#include "td/utils/UInt.h"
#include "td/utils/utf8.h"
#include <array>
#include <cstring>
#include <limits>
+#include <memory>
#include <string>
namespace td {
@@ -29,35 +31,14 @@ class TlParser {
size_t error_pos = std::numeric_limits<size_t>::max();
std::string error;
- unique_ptr<int32[]> data_buf;
+ std::unique_ptr<int32[]> data_buf;
static constexpr size_t SMALL_DATA_ARRAY_SIZE = 6;
std::array<int32, SMALL_DATA_ARRAY_SIZE> small_data_array;
alignas(4) static const unsigned char empty_data[sizeof(UInt256)];
public:
- explicit TlParser(Slice slice) {
- if (slice.size() % sizeof(int32) != 0) {
- set_error("Wrong length");
- return;
- }
-
- data_len = left_len = slice.size();
- if (is_aligned_pointer<4>(slice.begin())) {
- data = slice.ubegin();
- } else {
- int32 *buf;
- if (data_len <= small_data_array.size() * sizeof(int32)) {
- buf = &small_data_array[0];
- } else {
- LOG(ERROR) << "Unexpected big unaligned data pointer of length " << slice.size() << " at " << slice.begin();
- data_buf = make_unique<int32[]>(data_len / sizeof(int32));
- buf = data_buf.get();
- }
- std::memcpy(static_cast<void *>(buf), static_cast<const void *>(slice.begin()), slice.size());
- data = reinterpret_cast<unsigned char *>(buf);
- }
- }
+ explicit TlParser(Slice slice);
TlParser(const TlParser &other) = delete;
TlParser &operator=(const TlParser &other) = delete;
@@ -90,8 +71,19 @@ class TlParser {
}
}
+ bool can_prefetch_int() const {
+ return get_left_len() >= sizeof(int32);
+ }
+
+ int32 prefetch_int_unsafe() const {
+ int32 result;
+ std::memcpy(&result, data, sizeof(int32));
+ return result;
+ }
+
int32 fetch_int_unsafe() {
- int32 result = *reinterpret_cast<const int32 *>(data);
+ int32 result;
+ std::memcpy(&result, data, sizeof(int32));
data += sizeof(int32);
return result;
}
@@ -103,7 +95,7 @@ class TlParser {
int64 fetch_long_unsafe() {
int64 result;
- std::memcpy(reinterpret_cast<unsigned char *>(&result), data, sizeof(int64));
+ std::memcpy(&result, data, sizeof(int64));
data += sizeof(int64);
return result;
}
@@ -115,7 +107,7 @@ class TlParser {
double fetch_double_unsafe() {
double result;
- std::memcpy(reinterpret_cast<unsigned char *>(&result), data, sizeof(double));
+ std::memcpy(&result, data, sizeof(double));
data += sizeof(double);
return result;
}
@@ -128,7 +120,7 @@ class TlParser {
template <class T>
T fetch_binary_unsafe() {
T result;
- std::memcpy(reinterpret_cast<unsigned char *>(&result), data, sizeof(T));
+ std::memcpy(&result, data, sizeof(T));
data += sizeof(T);
return result;
}
@@ -136,7 +128,7 @@ class TlParser {
template <class T>
T fetch_binary() {
static_assert(sizeof(T) <= sizeof(empty_data), "too big fetch_binary");
- static_assert(sizeof(T) % sizeof(int32) == 0, "wrong call to fetch_binary");
+ //static_assert(sizeof(T) % sizeof(int32) == 0, "wrong call to fetch_binary");
check_len(sizeof(T));
return fetch_binary_unsafe<T>();
}
@@ -145,29 +137,48 @@ class TlParser {
T fetch_string() {
check_len(sizeof(int32));
size_t result_len = *data;
- const char *result_begin;
+ const unsigned char *result_begin;
size_t result_aligned_len;
if (result_len < 254) {
- result_begin = reinterpret_cast<const char *>(data + 1);
+ result_begin = data + 1;
result_aligned_len = (result_len >> 2) << 2;
+ data += sizeof(int32);
} else if (result_len == 254) {
result_len = data[1] + (data[2] << 8) + (data[3] << 16);
- result_begin = reinterpret_cast<const char *>(data + 4);
+ result_begin = data + 4;
result_aligned_len = ((result_len + 3) >> 2) << 2;
+ data += sizeof(int32);
} else {
- set_error("Can't fetch string, 255 found");
- return T();
+ check_len(sizeof(int32));
+ auto result_len_uint64 = static_cast<uint64>(data[1]) + (static_cast<uint64>(data[2]) << 8) +
+ (static_cast<uint64>(data[3]) << 16) + (static_cast<uint64>(data[4]) << 24) +
+ (static_cast<uint64>(data[5]) << 32) + (static_cast<uint64>(data[6]) << 40) +
+ (static_cast<uint64>(data[7]) << 48);
+ if (result_len_uint64 > std::numeric_limits<size_t>::max() - 3) {
+ set_error("Too big string found");
+ return T();
+ }
+ result_len = static_cast<size_t>(result_len_uint64);
+ result_begin = data + 8;
+ result_aligned_len = ((result_len + 3) >> 2) << 2;
+ data += sizeof(int64);
}
check_len(result_aligned_len);
- data += result_aligned_len + sizeof(int32);
- return T(result_begin, result_len);
+ if (!error.empty()) {
+ return T();
+ }
+ data += result_aligned_len;
+ return T(reinterpret_cast<const char *>(result_begin), result_len);
}
template <class T>
T fetch_string_raw(const size_t size) {
- CHECK(size % sizeof(int32) == 0);
+ //CHECK(size % sizeof(int32) == 0);
check_len(size);
- const char *result = reinterpret_cast<const char *>(data);
+ if (!error.empty()) {
+ return T();
+ }
+ auto result = reinterpret_cast<const char *>(data);
data += size;
return T(result, size);
}
@@ -187,6 +198,7 @@ class TlBufferParser : public TlParser {
public:
explicit TlBufferParser(const BufferSlice *buffer_slice) : TlParser(buffer_slice->as_slice()), parent_(buffer_slice) {
}
+
template <class T>
T fetch_string() {
auto result = TlParser::fetch_string<T>();
@@ -213,6 +225,7 @@ class TlBufferParser : public TlParser {
return T();
}
+
template <class T>
T fetch_string_raw(const size_t size) {
return TlParser::fetch_string_raw<T>(size);
@@ -221,12 +234,7 @@ class TlBufferParser : public TlParser {
private:
const BufferSlice *parent_;
- BufferSlice as_buffer_slice(Slice slice) {
- if (is_aligned_pointer<4>(slice.data())) {
- return parent_->from_slice(slice);
- }
- return BufferSlice(slice);
- }
+ BufferSlice as_buffer_slice(Slice slice);
};
template <>
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_storers.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_storers.h
index f389451d8a..6264226a01 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_storers.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/tl_storers.h
@@ -1,14 +1,13 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/int_types.h"
+#include "td/utils/common.h"
#include "td/utils/logging.h"
-#include "td/utils/misc.h"
#include "td/utils/Slice.h"
#include "td/utils/StorerBase.h"
@@ -17,11 +16,10 @@
namespace td {
class TlStorerUnsafe {
- char *buf;
+ unsigned char *buf_;
public:
- explicit TlStorerUnsafe(char *buf) : buf(buf) {
- CHECK(is_aligned_pointer<4>(buf));
+ explicit TlStorerUnsafe(unsigned char *buf) : buf_(buf) {
}
TlStorerUnsafe(const TlStorerUnsafe &other) = delete;
@@ -29,13 +27,12 @@ class TlStorerUnsafe {
template <class T>
void store_binary(const T &x) {
- std::memcpy(buf, reinterpret_cast<const unsigned char *>(&x), sizeof(T));
- buf += sizeof(T);
+ std::memcpy(buf_, &x, sizeof(T));
+ buf_ += sizeof(T);
}
void store_int(int32 x) {
- *reinterpret_cast<int32 *>(buf) = x;
- buf += sizeof(int32);
+ store_binary<int32>(x);
}
void store_long(int64 x) {
@@ -43,45 +40,55 @@ class TlStorerUnsafe {
}
void store_slice(Slice slice) {
- std::memcpy(buf, slice.begin(), slice.size());
- buf += slice.size();
+ std::memcpy(buf_, slice.begin(), slice.size());
+ buf_ += slice.size();
}
+
void store_storer(const Storer &storer) {
- size_t size = storer.store(reinterpret_cast<unsigned char *>(buf));
- buf += size;
+ size_t size = storer.store(buf_);
+ buf_ += size;
}
template <class T>
void store_string(const T &str) {
size_t len = str.size();
if (len < 254) {
- *buf++ = static_cast<char>(len);
+ *buf_++ = static_cast<unsigned char>(len);
len++;
} else if (len < (1 << 24)) {
- *buf++ = static_cast<char>(static_cast<unsigned char>(254));
- *buf++ = static_cast<char>(len & 255);
- *buf++ = static_cast<char>((len >> 8) & 255);
- *buf++ = static_cast<char>(len >> 16);
+ *buf_++ = static_cast<unsigned char>(254);
+ *buf_++ = static_cast<unsigned char>(len & 255);
+ *buf_++ = static_cast<unsigned char>((len >> 8) & 255);
+ *buf_++ = static_cast<unsigned char>(len >> 16);
+ } else if (static_cast<uint64>(len) < (static_cast<uint64>(1) << 32)) {
+ *buf_++ = static_cast<unsigned char>(255);
+ *buf_++ = static_cast<unsigned char>(len & 255);
+ *buf_++ = static_cast<unsigned char>((len >> 8) & 255);
+ *buf_++ = static_cast<unsigned char>((len >> 16) & 255);
+ *buf_++ = static_cast<unsigned char>((len >> 24) & 255);
+ *buf_++ = static_cast<unsigned char>(0);
+ *buf_++ = static_cast<unsigned char>(0);
+ *buf_++ = static_cast<unsigned char>(0);
} else {
LOG(FATAL) << "String size " << len << " is too big to be stored";
}
- std::memcpy(buf, str.data(), str.size());
- buf += str.size();
+ std::memcpy(buf_, str.data(), str.size());
+ buf_ += str.size();
switch (len & 3) {
case 1:
- *buf++ = '\0';
- // fallthrough
+ *buf_++ = 0;
+ // fallthrough
case 2:
- *buf++ = '\0';
- // fallthrough
+ *buf_++ = 0;
+ // fallthrough
case 3:
- *buf++ = '\0';
+ *buf_++ = 0;
}
}
- char *get_buf() const {
- return buf;
+ unsigned char *get_buf() const {
+ return buf_;
}
};
@@ -119,8 +126,10 @@ class TlStorerCalcLength {
size_t add = str.size();
if (add < 254) {
add += 1;
- } else {
+ } else if (add < (1 << 24)) {
add += 4;
+ } else {
+ add += 8;
}
add = (add + 3) & -4;
length += add;
@@ -131,138 +140,6 @@ class TlStorerCalcLength {
}
};
-class TlStorerToString {
- std::string result;
- int shift = 0;
-
- void store_field_begin(const char *name) {
- for (int i = 0; i < shift; i++) {
- result += ' ';
- }
- if (name && name[0]) {
- result += name;
- result += " = ";
- }
- }
-
- void store_field_end() {
- result += "\n";
- }
-
- void store_long(int64 value) {
- result += (PSLICE() << value).c_str();
- }
-
- void store_binary(Slice data) {
- static const char *hex = "0123456789ABCDEF";
-
- result.append("{ ");
- for (auto c : data) {
- unsigned char byte = c;
- result += hex[byte >> 4];
- result += hex[byte & 15];
- result += ' ';
- }
- result.append("}");
- }
-
- public:
- TlStorerToString() = default;
- TlStorerToString(const TlStorerToString &other) = delete;
- TlStorerToString &operator=(const TlStorerToString &other) = delete;
-
- void store_field(const char *name, bool value) {
- store_field_begin(name);
- result += (value ? "true" : "false");
- store_field_end();
- }
-
- void store_field(const char *name, int32 value) {
- store_field(name, static_cast<int64>(value));
- }
-
- void store_field(const char *name, int64 value) {
- store_field_begin(name);
- store_long(value);
- store_field_end();
- }
-
- void store_field(const char *name, double value) {
- store_field_begin(name);
- result += (PSLICE() << value).c_str();
- store_field_end();
- }
-
- void store_field(const char *name, const char *value) {
- store_field_begin(name);
- result += value;
- store_field_end();
- }
-
- void store_field(const char *name, const string &value) {
- store_field_begin(name);
- result += '"';
- result.append(value.data(), value.size());
- result += '"';
- store_field_end();
- }
-
- template <class T>
- void store_field(const char *name, const T &value) {
- store_field_begin(name);
- result.append(value.data(), value.size());
- store_field_end();
- }
-
- template <class BytesT>
- void store_bytes_field(const char *name, const BytesT &value) {
- static const char *hex = "0123456789ABCDEF";
-
- store_field_begin(name);
- result.append("bytes { ");
- for (size_t i = 0; i < value.size(); i++) {
- int b = value[static_cast<int>(i)] & 0xff;
- result += hex[b >> 4];
- result += hex[b & 15];
- result += ' ';
- }
- result.append("}");
- store_field_end();
- }
-
- void store_field(const char *name, const UInt128 &value) {
- store_field_begin(name);
- store_binary(Slice(reinterpret_cast<const unsigned char *>(&value), sizeof(value)));
- store_field_end();
- }
-
- void store_field(const char *name, const UInt256 &value) {
- store_field_begin(name);
- store_binary(Slice(reinterpret_cast<const unsigned char *>(&value), sizeof(value)));
- store_field_end();
- }
-
- void store_class_begin(const char *field_name, const char *class_name) {
- store_field_begin(field_name);
- result += class_name;
- result += " {\n";
- shift += 2;
- }
-
- void store_class_end() {
- shift -= 2;
- for (int i = 0; i < shift; i++) {
- result += ' ';
- }
- result += "}\n";
- CHECK(shift >= 0);
- }
-
- std::string str() const {
- return result;
- }
-};
-
template <class T>
size_t tl_calc_length(const T &data) {
TlStorerCalcLength storer_calc_length;
@@ -270,12 +147,14 @@ size_t tl_calc_length(const T &data) {
return storer_calc_length.get_length();
}
-template <class T, class CharT>
-size_t tl_store_unsafe(const T &data, CharT *dst) {
- char *start = reinterpret_cast<char *>(dst);
- TlStorerUnsafe storer_unsafe(start);
+template <class T>
+size_t tl_store_unsafe(const T &data, unsigned char *dst) TD_WARN_UNUSED_RESULT;
+
+template <class T>
+size_t tl_store_unsafe(const T &data, unsigned char *dst) {
+ TlStorerUnsafe storer_unsafe(dst);
data.store(storer_unsafe);
- return storer_unsafe.get_buf() - start;
+ return static_cast<size_t>(storer_unsafe.get_buf() - dst);
}
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/translit.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/translit.cpp
new file mode 100644
index 0000000000..65cfcf0074
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/translit.cpp
@@ -0,0 +1,115 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/translit.h"
+
+#include "td/utils/algorithm.h"
+#include "td/utils/FlatHashMap.h"
+#include "td/utils/misc.h"
+#include "td/utils/utf8.h"
+
+#include <algorithm>
+#include <utility>
+
+namespace td {
+
+static const FlatHashMap<uint32, string> &get_en_to_ru_simple_rules() {
+ static const FlatHashMap<uint32, string> rules{
+ {'a', "а"}, {'b', "б"}, {'c', "к"}, {'d', "д"}, {'e', "е"}, {'f', "ф"}, {'g', "г"}, {'h', "х"}, {'i', "и"},
+ {'j', "й"}, {'k', "к"}, {'l', "л"}, {'m', "м"}, {'n', "н"}, {'o', "о"}, {'p', "п"}, {'q', "к"}, {'r', "р"},
+ {'s', "с"}, {'t', "т"}, {'u', "у"}, {'v', "в"}, {'w', "в"}, {'x', "кс"}, {'y', "и"}, {'z', "з"}};
+ return rules;
+}
+
+static const std::vector<std::pair<string, string>> &get_en_to_ru_complex_rules() {
+ static const std::vector<std::pair<string, string>> rules{
+ {"ch", "ч"}, {"ei", "ей"}, {"ey", "ей"}, {"ia", "ия"}, {"iy", "ий"}, {"jo", "е"},
+ {"ju", "ю"}, {"ja", "я"}, {"kh", "х"}, {"shch", "щ"}, {"sh", "ш"}, {"sch", "щ"},
+ {"ts", "ц"}, {"yo", "е"}, {"yu", "ю"}, {"ya", "я"}, {"zh", "ж"}};
+ return rules;
+}
+
+static const FlatHashMap<uint32, string> &get_ru_to_en_simple_rules() {
+ static const FlatHashMap<uint32, string> rules{
+ {0x430, "a"}, {0x431, "b"}, {0x432, "v"}, {0x433, "g"}, {0x434, "d"}, {0x435, "e"}, {0x451, "e"},
+ {0x436, "zh"}, {0x437, "z"}, {0x438, "i"}, {0x439, "y"}, {0x43a, "k"}, {0x43b, "l"}, {0x43c, "m"},
+ {0x43d, "n"}, {0x43e, "o"}, {0x43f, "p"}, {0x440, "r"}, {0x441, "s"}, {0x442, "t"}, {0x443, "u"},
+ {0x444, "f"}, {0x445, "kh"}, {0x446, "ts"}, {0x447, "ch"}, {0x448, "sh"}, {0x449, "sch"}, {0x44a, ""},
+ {0x44b, "y"}, {0x44c, ""}, {0x44d, "e"}, {0x44e, "yu"}, {0x44f, "ya"}};
+ return rules;
+}
+
+static const std::vector<std::pair<string, string>> &get_ru_to_en_complex_rules() {
+ static const std::vector<std::pair<string, string>> rules{
+ {"ий", "y"}, {"ия", "ia"}, {"кс", "x"}, {"yo", "e"}, {"jo", "e"}};
+ return rules;
+}
+
+static void add_word_transliterations(vector<string> &result, Slice word, bool allow_partial,
+ const FlatHashMap<uint32, string> &simple_rules,
+ const vector<std::pair<string, string>> &complex_rules) {
+ string s;
+ auto pos = word.ubegin();
+ auto end = word.uend();
+ while (pos != end) {
+ uint32 code;
+ pos = next_utf8_unsafe(pos, &code);
+ auto it = simple_rules.find(code);
+ if (it != simple_rules.end()) {
+ s += it->second;
+ } else {
+ append_utf8_character(s, code);
+ }
+ }
+ if (!s.empty()) {
+ result.push_back(std::move(s));
+ s.clear();
+ }
+
+ pos = word.ubegin();
+ while (pos != end) {
+ auto suffix = Slice(pos, end);
+ bool found = false;
+ for (auto &rule : complex_rules) {
+ if (begins_with(suffix, rule.first)) {
+ found = true;
+ pos += rule.first.size();
+ s.append(rule.second);
+ break;
+ }
+ if (allow_partial && begins_with(rule.first, suffix)) {
+ result.push_back(s + rule.second);
+ }
+ }
+ if (found) {
+ continue;
+ }
+
+ uint32 code;
+ pos = next_utf8_unsafe(pos, &code);
+ auto it = simple_rules.find(code);
+ if (it != simple_rules.end()) {
+ s += it->second;
+ } else {
+ append_utf8_character(s, code);
+ }
+ }
+ if (!s.empty()) {
+ result.push_back(std::move(s));
+ }
+}
+
+vector<string> get_word_transliterations(Slice word, bool allow_partial) {
+ vector<string> result;
+
+ add_word_transliterations(result, word, allow_partial, get_en_to_ru_simple_rules(), get_en_to_ru_complex_rules());
+ add_word_transliterations(result, word, allow_partial, get_ru_to_en_simple_rules(), get_ru_to_en_complex_rules());
+
+ td::unique(result);
+ return result;
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/translit.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/translit.h
new file mode 100644
index 0000000000..e72cb5c8e5
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/translit.h
@@ -0,0 +1,16 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/common.h"
+#include "td/utils/Slice.h"
+
+namespace td {
+
+vector<string> get_word_transliterations(Slice word, bool allow_partial);
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/type_traits.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/type_traits.h
index ef9c159420..71bbf77b79 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/type_traits.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/type_traits.h
@@ -1,22 +1,41 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
+#include "td/utils/int_types.h"
+
+#include <type_traits>
+
namespace td {
template <class FunctionT>
struct member_function_class;
-template <class ReturnType, class Type>
-struct member_function_class<ReturnType Type::*> {
+template <class ReturnType, class Type, class... Args>
+struct member_function_class<ReturnType (Type::*)(Args...)> {
using type = Type;
+ static constexpr size_t argument_count() {
+ return sizeof...(Args);
+ }
};
template <class FunctionT>
using member_function_class_t = typename member_function_class<FunctionT>::type;
+template <class FunctionT>
+constexpr size_t member_function_argument_count() {
+ return member_function_class<FunctionT>::argument_count();
+}
+
+// no std::is_trivially_copyable in libstdc++ before 5.0
+#if __GLIBCXX__
+#define TD_IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T)
+#else
+#define TD_IS_TRIVIALLY_COPYABLE(T) ::std::is_trivially_copyable<T>::value
+#endif
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/uint128.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/uint128.h
new file mode 100644
index 0000000000..902900ef27
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/uint128.h
@@ -0,0 +1,293 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include "td/utils/bits.h"
+#include "td/utils/common.h"
+
+#include <limits>
+#include <type_traits>
+
+namespace td {
+
+class uint128_emulated {
+ public:
+ using uint128 = uint128_emulated;
+ uint128_emulated(uint64 hi, uint64 lo) : hi_(hi), lo_(lo) {
+ }
+ template <class T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
+ uint128_emulated(T lo) : uint128_emulated(0, lo) {
+ }
+ uint128_emulated() = default;
+
+ uint64 hi() const {
+ return hi_;
+ }
+ uint64 lo() const {
+ return lo_;
+ }
+ uint64 rounded_hi() const {
+ return hi_ + (lo_ >> 63);
+ }
+ static uint128 from_signed(int64 x) {
+ if (x >= 0) {
+ return uint128(0, x);
+ }
+ return uint128(std::numeric_limits<uint64>::max(), static_cast<uint64>(x));
+ }
+ static uint128 from_unsigned(uint64 x) {
+ return uint128(0, x);
+ }
+
+ uint128 add(uint128 other) const {
+ uint128 res(other.hi() + hi(), other.lo() + lo());
+ if (res.lo() < lo()) {
+ res.hi_++;
+ }
+ return res;
+ }
+
+ uint128 shl(int cnt) const {
+ if (cnt == 0) {
+ return *this;
+ }
+ if (cnt < 64) {
+ return uint128((hi() << cnt) | (lo() >> (64 - cnt)), lo() << cnt);
+ }
+ if (cnt < 128) {
+ return uint128(lo() << (cnt - 64), 0);
+ }
+ return uint128();
+ }
+ uint128 shr(int cnt) const {
+ if (cnt == 0) {
+ return *this;
+ }
+ if (cnt < 64) {
+ return uint128(hi() >> cnt, (lo() >> cnt) | (hi() << (64 - cnt)));
+ }
+ if (cnt < 128) {
+ return uint128(0, hi() >> (cnt - 64));
+ }
+ return uint128();
+ }
+
+ uint128 mult(uint128 other) const {
+ uint64 a_lo = lo() & 0xffffffff;
+ uint64 a_hi = lo() >> 32;
+ uint64 b_lo = other.lo() & 0xffffffff;
+ uint64 b_hi = other.lo() >> 32;
+ uint128 res(lo() * other.hi() + hi() * other.lo() + a_hi * b_hi, a_lo * b_lo);
+ uint128 add1(0, a_lo * b_hi);
+ uint128 add2(0, a_hi * b_lo);
+ return res.add(add1.shl(32)).add(add2.shl(32));
+ }
+ uint128 mult(uint64 other) const {
+ return mult(uint128(0, other));
+ }
+ uint128 mult_signed(int64 other) const {
+ return mult(uint128::from_signed(other));
+ }
+ bool is_zero() const {
+ return lo() == 0 && hi() == 0;
+ }
+ uint128 sub(uint128 other) const {
+ uint32 carry = 0;
+ if (other.lo() > lo()) {
+ carry = 1;
+ }
+ return uint128(hi() - other.hi() - carry, lo() - other.lo());
+ }
+ void divmod(uint128 other, uint128 *div_res, uint128 *mod_res) const {
+ CHECK(!other.is_zero());
+
+ auto from = *this;
+ auto ctz = from.count_leading_zeroes();
+ auto other_ctz = other.count_leading_zeroes();
+ if (ctz > other_ctz) {
+ *div_res = uint128();
+ *mod_res = from;
+ return;
+ }
+ auto shift = other_ctz - ctz;
+ auto res = uint128();
+ for (int i = shift; i >= 0; i--) {
+ auto sub = other.shl(i);
+ res = res.shl(1);
+ if (from.greater_or_equal(sub)) {
+ from = from.sub(sub);
+ res = res.set_lower_bit();
+ }
+ }
+
+ *div_res = res;
+ *mod_res = from;
+ }
+ uint128 div(uint128 other) const {
+ uint128 a;
+ uint128 b;
+ divmod(other, &a, &b);
+ return a;
+ }
+ uint128 mod(uint128 other) const {
+ uint128 a;
+ uint128 b;
+ divmod(other, &a, &b);
+ return b;
+ }
+
+ void divmod_signed(int64 y, int64 *quot, int64 *rem) const {
+ CHECK(y != 0);
+ auto x = *this;
+ int x_sgn = x.is_negative();
+ int y_sgn = y < 0;
+ if (x_sgn) {
+ x = x.negate();
+ }
+ uint128 uy = from_signed(y);
+ if (uy.is_negative()) {
+ uy = uy.negate();
+ }
+
+ uint128 t_quot;
+ uint128 t_mod;
+ x.divmod(uy, &t_quot, &t_mod);
+ *quot = t_quot.lo();
+ *rem = t_mod.lo();
+ if (x_sgn != y_sgn) {
+ *quot = -*quot;
+ }
+ if (x_sgn) {
+ *rem = -*rem;
+ }
+ }
+
+ private:
+ uint64 hi_{0};
+ uint64 lo_{0};
+
+ bool is_negative() const {
+ return (hi_ >> 63) == 1;
+ }
+
+ int32 count_leading_zeroes() const {
+ if (hi() == 0) {
+ return 64 + count_leading_zeroes64(lo());
+ }
+ return count_leading_zeroes64(hi());
+ }
+ uint128 set_lower_bit() const {
+ return uint128(hi(), lo() | 1);
+ }
+ bool greater_or_equal(uint128 other) const {
+ return hi() > other.hi() || (hi() == other.hi() && lo() >= other.lo());
+ }
+ uint128 negate() const {
+ uint128 res(~hi(), ~lo() + 1);
+ if (res.lo() == 0) {
+ return uint128(res.hi() + 1, 0);
+ }
+ return res;
+ }
+};
+
+#if TD_HAVE_INT128
+class uint128_intrinsic {
+ public:
+ using ValueT = unsigned __int128;
+ using uint128 = uint128_intrinsic;
+ explicit uint128_intrinsic(ValueT value) : value_(value) {
+ }
+ uint128_intrinsic(uint64 hi, uint64 lo) : value_((ValueT(hi) << 64) | lo) {
+ }
+ uint128_intrinsic() = default;
+
+ static uint128 from_signed(int64 x) {
+ return uint128(static_cast<ValueT>(x));
+ }
+ static uint128 from_unsigned(uint64 x) {
+ return uint128(static_cast<ValueT>(x));
+ }
+ uint64 hi() const {
+ return uint64(value() >> 64);
+ }
+ uint64 lo() const {
+ return uint64(value() & std::numeric_limits<uint64>::max());
+ }
+ uint64 rounded_hi() const {
+ return uint64((value() + (1ULL << 63)) >> 64);
+ }
+ uint128 add(uint128 other) const {
+ return uint128(value() + other.value());
+ }
+ uint128 sub(uint128 other) const {
+ return uint128(value() - other.value());
+ }
+
+ uint128 shl(int cnt) const {
+ if (cnt >= 128) {
+ return uint128();
+ }
+ return uint128(value() << cnt);
+ }
+
+ uint128 shr(int cnt) const {
+ if (cnt >= 128) {
+ return uint128();
+ }
+ return uint128(value() >> cnt);
+ }
+
+ uint128 mult(uint128 other) const {
+ return uint128(value() * other.value());
+ }
+ uint128 mult(uint64 other) const {
+ return uint128(value() * other);
+ }
+ uint128 mult_signed(int64 other) const {
+ return uint128(value() * other);
+ }
+ bool is_zero() const {
+ return value() == 0;
+ }
+ void divmod(uint128 other, uint128 *div_res, uint128 *mod_res) const {
+ CHECK(!other.is_zero());
+ *div_res = uint128(value() / other.value());
+ *mod_res = uint128(value() % other.value());
+ }
+ uint128 div(uint128 other) const {
+ CHECK(!other.is_zero());
+ return uint128(value() / other.value());
+ }
+ uint128 mod(uint128 other) const {
+ CHECK(!other.is_zero());
+ return uint128(value() % other.value());
+ }
+ void divmod_signed(int64 y, int64 *quot, int64 *rem) const {
+ CHECK(y != 0);
+ *quot = static_cast<int64>(signed_value() / y);
+ *rem = static_cast<int64>(signed_value() % y);
+ }
+
+ private:
+ unsigned __int128 value_{0};
+ ValueT value() const {
+ return value_;
+ }
+ __int128 signed_value() const {
+ return static_cast<__int128>(value());
+ }
+};
+#endif
+
+#if TD_HAVE_INT128
+using uint128 = uint128_intrinsic;
+#else
+using uint128 = uint128_emulated;
+#endif
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.cpp
index 11e76b7979..1b16809f0c 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,130 +8,209 @@
#include "td/utils/logging.h"
-#include <algorithm>
-#include <iterator>
-
namespace td {
// list of [(range_begin << 5) + range_type]
static const uint32 unicode_simple_category_ranges[] = {
- 0, 1028, 1056, 1538, 1856, 2081, 2912, 3105, 3936, 5124, 5152, 5441,
- 5472, 5699, 5760, 5793, 5824, 5923, 5953, 5984, 6019, 6112, 6145, 6880,
- 6913, 7904, 7937, 22592, 22721, 23104, 23553, 23712, 23937, 23968, 24001, 24032,
- 28161, 28320, 28353, 28416, 28481, 28608, 28641, 28672, 28865, 28896, 28929, 29024,
- 29057, 29088, 29121, 29760, 29793, 32448, 32481, 36928, 37185, 42496, 42529, 43744,
- 43809, 43840, 44065, 45312, 47617, 48480, 48641, 48736, 50177, 51552, 52226, 52544,
- 52673, 52736, 52769, 55936, 55969, 56000, 56481, 56544, 56769, 56834, 57153, 57248,
- 57313, 57344, 57857, 57888, 57921, 58880, 59809, 62656, 63009, 63040, 63490, 63809,
- 64864, 65153, 65216, 65345, 65376, 65537, 66240, 66369, 66400, 66689, 66720, 66817,
- 66848, 67585, 68384, 70657, 71328, 71361, 71616, 73857, 75584, 75681, 75712, 76289,
- 76320, 76545, 76864, 76994, 77312, 77345, 77856, 77985, 78240, 78305, 78368, 78433,
- 79136, 79169, 79392, 79425, 79456, 79553, 79680, 79777, 79808, 80321, 80352, 80769,
- 80832, 80865, 80960, 81090, 81409, 81472, 81539, 81728, 82081, 82272, 82401, 82464,
- 82529, 83232, 83265, 83488, 83521, 83584, 83617, 83680, 83713, 83776, 84769, 84896,
- 84929, 84960, 85186, 85504, 85569, 85664, 86177, 86464, 86497, 86592, 86625, 87328,
- 87361, 87584, 87617, 87680, 87713, 87872, 87969, 88000, 88577, 88608, 89089, 89152,
- 89282, 89600, 89889, 89920, 90273, 90528, 90593, 90656, 90721, 91424, 91457, 91680,
- 91713, 91776, 91809, 91968, 92065, 92096, 93057, 93120, 93153, 93248, 93378, 93696,
- 93729, 93763, 93952, 94305, 94336, 94369, 94560, 94657, 94752, 94785, 94912, 95009,
- 95072, 95105, 95136, 95169, 95232, 95329, 95392, 95489, 95584, 95681, 96064, 96769,
- 96800, 97474, 97795, 97888, 98465, 98720, 98753, 98848, 98881, 99616, 99649, 100160,
- 100257, 100288, 101121, 101216, 101377, 101440, 101570, 101888, 102147, 102368, 102401, 102432,
- 102561, 102816, 102849, 102944, 102977, 103712, 103745, 104064, 104097, 104256, 104353, 104384,
- 105409, 105440, 105473, 105536, 105666, 105984, 106017, 106080, 106657, 106912, 106945, 107040,
- 107073, 108384, 108449, 108480, 108993, 109024, 109185, 109280, 109315, 109537, 109632, 109762,
- 110083, 110368, 110401, 110592, 110753, 111328, 111425, 112192, 112225, 112512, 112545, 112576,
- 112641, 112864, 113858, 114176, 114721, 116256, 116289, 116352, 116737, 116960, 117250, 117568,
- 118817, 118880, 118913, 118944, 119009, 119072, 119105, 119136, 119201, 119232, 119425, 119552,
- 119585, 119808, 119841, 119936, 119969, 120000, 120033, 120064, 120129, 120192, 120225, 120352,
- 120385, 120448, 120737, 120768, 120833, 120992, 121025, 121056, 121346, 121664, 121729, 121856,
- 122881, 122912, 123906, 124227, 124544, 124929, 125184, 125217, 126368, 127233, 127392, 131073,
- 132448, 133089, 133122, 133440, 133633, 133824, 133953, 134080, 134177, 134208, 134305, 134368,
- 134593, 134688, 134817, 135232, 135617, 135648, 135682, 136000, 136193, 137408, 137441, 137472,
- 137633, 137664, 137729, 139104, 139137, 149792, 149825, 149952, 150017, 150240, 150273, 150304,
- 150337, 150464, 150529, 151840, 151873, 152000, 152065, 153120, 153153, 153280, 153345, 153568,
- 153601, 153632, 153665, 153792, 153857, 154336, 154369, 156192, 156225, 156352, 156417, 158560,
- 159011, 159648, 159745, 160256, 160769, 163520, 163585, 163776, 163873, 183712, 183777, 184324,
- 184353, 185184, 185345, 187744, 187843, 187937, 188192, 188417, 188832, 188865, 188992, 189441,
- 190016, 190465, 191040, 191489, 191904, 191937, 192032, 192513, 194176, 195297, 195328, 195457,
- 195488, 195586, 195904, 196099, 196416, 197122, 197440, 197633, 200448, 200705, 200864, 200929,
- 202016, 202049, 202080, 202241, 204480, 204801, 205792, 207042, 207361, 208320, 208385, 208544,
- 208897, 210304, 210433, 211264, 211458, 211779, 211808, 212993, 213728, 214017, 215712, 217090,
- 217408, 217602, 217920, 218337, 218368, 221345, 222848, 223393, 223616, 223746, 224064, 225377,
- 226336, 226753, 226818, 227137, 228544, 229377, 230528, 231426, 231744, 231841, 231938, 232257,
- 233408, 233473, 233760, 236833, 236960, 236993, 237120, 237217, 237280, 237569, 243712, 245761,
- 254656, 254721, 254912, 254977, 256192, 256257, 256448, 256513, 256768, 256801, 256832, 256865,
- 256896, 256929, 256960, 256993, 257984, 258049, 259744, 259777, 260000, 260033, 260064, 260161,
- 260256, 260289, 260512, 260609, 260736, 260801, 260992, 261121, 261536, 261697, 261792, 261825,
- 262048, 262148, 262496, 263428, 263488, 263652, 263680, 265188, 265216, 265731, 265761, 265792,
- 265859, 266048, 266209, 266243, 266560, 266753, 267168, 270401, 270432, 270561, 270592, 270657,
- 270976, 271009, 271040, 271137, 271296, 271489, 271520, 271553, 271584, 271617, 271648, 271681,
- 271808, 271841, 272192, 272257, 272384, 272545, 272704, 272833, 272864, 272899, 274529, 274595,
- 274752, 297987, 299904, 302403, 303104, 323267, 324224, 360449, 361952, 361985, 363488, 363521,
- 367776, 367969, 368096, 368193, 368256, 368547, 368576, 368641, 369856, 369889, 369920, 370081,
- 370112, 370177, 371968, 372193, 372224, 372737, 373472, 373761, 373984, 374017, 374240, 374273,
- 374496, 374529, 374752, 374785, 375008, 375041, 375264, 375297, 375520, 375553, 375776, 378337,
- 378368, 393220, 393248, 393377, 393443, 393472, 394275, 394560, 394785, 394944, 395011, 395105,
- 395168, 395297, 398048, 398241, 398336, 398369, 401248, 401281, 401408, 401569, 402880, 402977,
- 405984, 406083, 406208, 406529, 407392, 409089, 409600, 410627, 410944, 411907, 412160, 412195,
- 412672, 413699, 414016, 415267, 415744, 425985, 636608, 638977, 1309376, 1310721, 1348000, 1350145,
- 1351616, 1351681, 1360288, 1360385, 1360898, 1361217, 1361280, 1361921, 1363424, 1363937, 1364928, 1364993,
- 1367235, 1367552, 1368801, 1369088, 1369153, 1372448, 1372513, 1373664, 1373697, 1373952, 1375969, 1376320,
- 1376353, 1376448, 1376481, 1376608, 1376641, 1377376, 1377795, 1377984, 1378305, 1379968, 1380417, 1382016,
- 1382914, 1383232, 1384001, 1384192, 1384289, 1384320, 1384353, 1384384, 1384450, 1384769, 1385664, 1385985,
- 1386720, 1387521, 1388448, 1388673, 1390176, 1391073, 1391106, 1391424, 1391617, 1391776, 1391809, 1392130,
- 1392449, 1392608, 1392641, 1393952, 1394689, 1394784, 1394817, 1395072, 1395202, 1395520, 1395713, 1396448,
- 1396545, 1396576, 1396673, 1398272, 1398305, 1398336, 1398433, 1398496, 1398561, 1398720, 1398785, 1398816,
- 1398849, 1398880, 1399649, 1399744, 1399809, 1400160, 1400385, 1400480, 1400865, 1401056, 1401121, 1401312,
- 1401377, 1401568, 1401857, 1402080, 1402113, 1402336, 1402369, 1403744, 1403777, 1404096, 1404417, 1408096,
- 1408514, 1408832, 1409025, 1766528, 1766913, 1767648, 1767777, 1769344, 2039809, 2051520, 2051585, 2054976,
- 2056193, 2056416, 2056801, 2056960, 2057121, 2057152, 2057185, 2057504, 2057537, 2057952, 2057985, 2058144,
- 2058177, 2058208, 2058241, 2058304, 2058337, 2058400, 2058433, 2061888, 2062945, 2074560, 2075137, 2077184,
- 2077249, 2078976, 2080257, 2080640, 2084353, 2084512, 2084545, 2088864, 2089474, 2089792, 2090017, 2090848,
- 2091041, 2091872, 2092225, 2095072, 2095169, 2095360, 2095425, 2095616, 2095681, 2095872, 2095937, 2096032,
- 2097153, 2097536, 2097569, 2098400, 2098433, 2099040, 2099073, 2099136, 2099169, 2099648, 2099713, 2100160,
- 2101249, 2105184, 2105571, 2107008, 2107395, 2109216, 2109763, 2109824, 2117633, 2118560, 2118657, 2120224,
- 2120739, 2121600, 2121729, 2122755, 2122880, 2123265, 2123811, 2123841, 2124099, 2124128, 2124289, 2125504,
- 2125825, 2126784, 2126849, 2128000, 2128129, 2128384, 2128419, 2128576, 2129921, 2134976, 2135042, 2135360,
- 2135553, 2136704, 2136833, 2137984, 2138113, 2139392, 2139649, 2141312, 2146305, 2156256, 2156545, 2157248,
- 2157569, 2157824, 2162689, 2162880, 2162945, 2162976, 2163009, 2164416, 2164449, 2164512, 2164609, 2164640,
- 2164705, 2165440, 2165507, 2165761, 2166496, 2166563, 2166785, 2167776, 2168035, 2168320, 2169857, 2170464,
- 2170497, 2170560, 2170723, 2170881, 2171587, 2171776, 2171905, 2172736, 2174977, 2176768, 2176899, 2176961,
- 2177027, 2177536, 2177603, 2179073, 2179104, 2179585, 2179712, 2179745, 2179840, 2179873, 2180736, 2181123,
- 2181376, 2182145, 2183075, 2183136, 2183169, 2184099, 2184192, 2185217, 2185472, 2185505, 2186400, 2186595,
- 2186752, 2187265, 2188992, 2189313, 2190016, 2190083, 2190337, 2190944, 2191107, 2191361, 2191936, 2192675,
- 2192896, 2195457, 2197792, 2199553, 2201184, 2201601, 2203232, 2203459, 2203648, 2214915, 2215904, 2228321,
- 2230016, 2230851, 2231490, 2231808, 2232417, 2233856, 2234881, 2235680, 2235906, 2236224, 2236513, 2237664,
- 2238146, 2238464, 2238977, 2240096, 2240193, 2240224, 2240609, 2242144, 2242593, 2242720, 2243074, 2243393,
- 2243424, 2243457, 2243488, 2243619, 2244256, 2244609, 2245184, 2245217, 2246016, 2248705, 2248928, 2248961,
- 2248992, 2249025, 2249152, 2249185, 2249664, 2249697, 2250016, 2250241, 2251744, 2252290, 2252608, 2252961,
- 2253216, 2253281, 2253344, 2253409, 2254112, 2254145, 2254368, 2254401, 2254464, 2254497, 2254656, 2254753,
- 2254784, 2255361, 2255392, 2255777, 2255936, 2260993, 2262688, 2263265, 2263392, 2263554, 2263872, 2265089,
- 2266624, 2267265, 2267328, 2267361, 2267392, 2267650, 2267968, 2273281, 2274784, 2276097, 2276224, 2277377,
- 2278912, 2279553, 2279584, 2279938, 2280256, 2281473, 2282848, 2283522, 2283840, 2285569, 2286400, 2287106,
- 2287427, 2287488, 2298881, 2300930, 2301251, 2301536, 2301921, 2301952, 2316289, 2318112, 2326529, 2326816,
- 2326849, 2328032, 2328577, 2328608, 2329090, 2329411, 2330016, 2330177, 2331136, 2359297, 2388800, 2392067,
- 2395616, 2396161, 2402432, 2490369, 2524640, 2654209, 2672864, 2949121, 2967328, 2967553, 2968544, 2968578,
- 2968896, 2972161, 2973120, 2973697, 2975232, 2975745, 2975872, 2976258, 2976576, 2976611, 2976832, 2976865,
- 2977536, 2977697, 2978304, 3006465, 3008672, 3009025, 3009056, 3011169, 3011584, 3013633, 3013664, 3014657,
- 3210656, 3211265, 3235424, 3538945, 3539008, 3637249, 3640672, 3640833, 3641248, 3641345, 3641632, 3641857,
- 3642176, 3828739, 3829312, 3833857, 3836576, 3836609, 3838880, 3838913, 3838976, 3839041, 3839072, 3839137,
- 3839200, 3839265, 3839392, 3839425, 3839808, 3839841, 3839872, 3839905, 3840128, 3840161, 3842240, 3842273,
- 3842400, 3842465, 3842720, 3842753, 3842976, 3843009, 3843904, 3843937, 3844064, 3844097, 3844256, 3844289,
- 3844320, 3844417, 3844640, 3844673, 3855552, 3855617, 3856416, 3856449, 3857248, 3857281, 3858272, 3858305,
- 3859104, 3859137, 3860128, 3860161, 3860960, 3860993, 3861984, 3862017, 3862816, 3862849, 3863840, 3863873,
- 3864672, 3864705, 3864960, 3865026, 3866624, 3997697, 4004000, 4004067, 4004352, 4005889, 4008064, 4008450,
- 4008768, 4046849, 4046976, 4047009, 4047872, 4047905, 4047968, 4048001, 4048032, 4048097, 4048128, 4048161,
- 4048480, 4048513, 4048640, 4048673, 4048704, 4048737, 4048768, 4048961, 4048992, 4049121, 4049152, 4049185,
- 4049216, 4049249, 4049280, 4049313, 4049408, 4049441, 4049504, 4049537, 4049568, 4049633, 4049664, 4049697,
- 4049728, 4049761, 4049792, 4049825, 4049856, 4049889, 4049920, 4049953, 4050016, 4050049, 4050080, 4050145,
- 4050272, 4050305, 4050528, 4050561, 4050688, 4050721, 4050848, 4050881, 4050912, 4050945, 4051264, 4051297,
- 4051840, 4052001, 4052096, 4052129, 4052288, 4052321, 4052864, 4071427, 4071840, 4194305, 5561056, 5562369,
- 5695136, 5695489, 5702592, 5702657, 5887040, 6225921, 6243264, 4294967295};
+ 0, 1028, 1056, 1538, 1856, 2081, 2912, 3105, 3936, 5124, 5152, 5441,
+ 5472, 5699, 5760, 5793, 5824, 5923, 5953, 5984, 6019, 6112, 6145, 6880,
+ 6913, 7904, 7937, 22592, 22721, 23104, 23553, 23712, 23937, 23968, 24001, 24032,
+ 28161, 28320, 28353, 28416, 28481, 28608, 28641, 28672, 28865, 28896, 28929, 29024,
+ 29057, 29088, 29121, 29760, 29793, 32448, 32481, 36928, 37185, 42496, 42529, 43744,
+ 43809, 43840, 44033, 45344, 47617, 48480, 48609, 48736, 50177, 51552, 52226, 52544,
+ 52673, 52736, 52769, 55936, 55969, 56000, 56481, 56544, 56769, 56834, 57153, 57248,
+ 57313, 57344, 57857, 57888, 57921, 58880, 59809, 62656, 63009, 63040, 63490, 63809,
+ 64864, 65153, 65216, 65345, 65376, 65537, 66240, 66369, 66400, 66689, 66720, 66817,
+ 66848, 67585, 68384, 68609, 68960, 69121, 69888, 69921, 70112, 70657, 72000, 73857,
+ 75584, 75681, 75712, 76289, 76320, 76545, 76864, 76994, 77312, 77345, 77856, 77985,
+ 78240, 78305, 78368, 78433, 79136, 79169, 79392, 79425, 79456, 79553, 79680, 79777,
+ 79808, 80321, 80352, 80769, 80832, 80865, 80960, 81090, 81409, 81472, 81539, 81728,
+ 81793, 81824, 82081, 82272, 82401, 82464, 82529, 83232, 83265, 83488, 83521, 83584,
+ 83617, 83680, 83713, 83776, 84769, 84896, 84929, 84960, 85186, 85504, 85569, 85664,
+ 86177, 86464, 86497, 86592, 86625, 87328, 87361, 87584, 87617, 87680, 87713, 87872,
+ 87969, 88000, 88577, 88608, 89089, 89152, 89282, 89600, 89889, 89920, 90273, 90528,
+ 90593, 90656, 90721, 91424, 91457, 91680, 91713, 91776, 91809, 91968, 92065, 92096,
+ 93057, 93120, 93153, 93248, 93378, 93696, 93729, 93763, 93952, 94305, 94336, 94369,
+ 94560, 94657, 94752, 94785, 94912, 95009, 95072, 95105, 95136, 95169, 95232, 95329,
+ 95392, 95489, 95584, 95681, 96064, 96769, 96800, 97474, 97795, 97888, 98465, 98720,
+ 98753, 98848, 98881, 99616, 99649, 100160, 100257, 100288, 101121, 101216, 101281, 101312,
+ 101377, 101440, 101570, 101888, 102147, 102368, 102401, 102432, 102561, 102816, 102849, 102944,
+ 102977, 103712, 103745, 104064, 104097, 104256, 104353, 104384, 105377, 105440, 105473, 105536,
+ 105666, 105984, 106017, 106080, 106625, 106912, 106945, 107040, 107073, 108384, 108449, 108480,
+ 108993, 109024, 109185, 109280, 109315, 109537, 109632, 109762, 110083, 110368, 110401, 110592,
+ 110753, 111328, 111425, 112192, 112225, 112512, 112545, 112576, 112641, 112864, 113858, 114176,
+ 114721, 116256, 116289, 116352, 116737, 116960, 117250, 117568, 118817, 118880, 118913, 118944,
+ 118977, 119136, 119169, 119936, 119969, 120000, 120033, 120352, 120385, 120448, 120737, 120768,
+ 120833, 120992, 121025, 121056, 121346, 121664, 121729, 121856, 122881, 122912, 123906, 124227,
+ 124544, 124929, 125184, 125217, 126368, 127233, 127392, 131073, 132448, 133089, 133122, 133440,
+ 133633, 133824, 133953, 134080, 134177, 134208, 134305, 134368, 134593, 134688, 134817, 135232,
+ 135617, 135648, 135682, 136000, 136193, 137408, 137441, 137472, 137633, 137664, 137729, 139104,
+ 139137, 149792, 149825, 149952, 150017, 150240, 150273, 150304, 150337, 150464, 150529, 151840,
+ 151873, 152000, 152065, 153120, 153153, 153280, 153345, 153568, 153601, 153632, 153665, 153792,
+ 153857, 154336, 154369, 156192, 156225, 156352, 156417, 158560, 159011, 159648, 159745, 160256,
+ 160769, 163520, 163585, 163776, 163873, 183712, 183777, 184324, 184353, 185184, 185345, 187744,
+ 187843, 187937, 188192, 188417, 188992, 189409, 190016, 190465, 191040, 191489, 191904, 191937,
+ 192032, 192513, 194176, 195297, 195328, 195457, 195488, 195586, 195904, 196099, 196416, 197122,
+ 197440, 197633, 200480, 200705, 200864, 200929, 202016, 202049, 202080, 202241, 204480, 204801,
+ 205792, 207042, 207361, 208320, 208385, 208544, 208897, 210304, 210433, 211264, 211458, 211779,
+ 211808, 212993, 213728, 214017, 215712, 217090, 217408, 217602, 217920, 218337, 218368, 221345,
+ 222848, 223393, 223648, 223746, 224064, 225377, 226336, 226753, 226818, 227137, 228544, 229377,
+ 230528, 231426, 231744, 231841, 231938, 232257, 233408, 233473, 233760, 233985, 235360, 235425,
+ 235520, 236833, 236960, 236993, 237184, 237217, 237280, 237377, 237408, 237569, 243712, 245761,
+ 254656, 254721, 254912, 254977, 256192, 256257, 256448, 256513, 256768, 256801, 256832, 256865,
+ 256896, 256929, 256960, 256993, 257984, 258049, 259744, 259777, 260000, 260033, 260064, 260161,
+ 260256, 260289, 260512, 260609, 260736, 260801, 260992, 261121, 261536, 261697, 261792, 261825,
+ 262048, 262148, 262496, 263428, 263488, 263652, 263680, 265188, 265216, 265731, 265761, 265792,
+ 265859, 266048, 266209, 266243, 266560, 266753, 267168, 270401, 270432, 270561, 270592, 270657,
+ 270976, 271009, 271040, 271137, 271296, 271489, 271520, 271553, 271584, 271617, 271648, 271681,
+ 271808, 271841, 272192, 272257, 272384, 272545, 272704, 272833, 272864, 272899, 274529, 274595,
+ 274752, 297987, 299904, 302403, 303104, 323267, 324224, 360449, 367776, 367969, 368096, 368193,
+ 368256, 368547, 368576, 368641, 369856, 369889, 369920, 370081, 370112, 370177, 371968, 372193,
+ 372224, 372737, 373472, 373761, 373984, 374017, 374240, 374273, 374496, 374529, 374752, 374785,
+ 375008, 375041, 375264, 375297, 375520, 375553, 375776, 378337, 378368, 393220, 393248, 393377,
+ 393443, 393472, 394275, 394560, 394785, 394944, 395011, 395105, 395168, 395297, 398048, 398241,
+ 398336, 398369, 401248, 401281, 401408, 401569, 402944, 402977, 405984, 406083, 406208, 406529,
+ 407552, 409089, 409600, 410627, 410944, 411907, 412160, 412195, 412672, 413699, 414016, 415267,
+ 415744, 425985, 636928, 638977, 1348000, 1350145, 1351616, 1351681, 1360288, 1360385, 1360898, 1361217,
+ 1361280, 1361921, 1363424, 1363937, 1364928, 1364993, 1367235, 1367552, 1368801, 1369088, 1369153, 1372448,
+ 1372513, 1374560, 1374721, 1374784, 1374817, 1374848, 1374881, 1375040, 1375809, 1376320, 1376353, 1376448,
+ 1376481, 1376608, 1376641, 1377376, 1377795, 1377984, 1378305, 1379968, 1380417, 1382016, 1382914, 1383232,
+ 1384001, 1384192, 1384289, 1384320, 1384353, 1384416, 1384450, 1384769, 1385664, 1385985, 1386720, 1387521,
+ 1388448, 1388673, 1390176, 1391073, 1391106, 1391424, 1391617, 1391776, 1391809, 1392130, 1392449, 1392608,
+ 1392641, 1393952, 1394689, 1394784, 1394817, 1395072, 1395202, 1395520, 1395713, 1396448, 1396545, 1396576,
+ 1396673, 1398272, 1398305, 1398336, 1398433, 1398496, 1398561, 1398720, 1398785, 1398816, 1398849, 1398880,
+ 1399649, 1399744, 1399809, 1400160, 1400385, 1400480, 1400865, 1401056, 1401121, 1401312, 1401377, 1401568,
+ 1401857, 1402080, 1402113, 1402336, 1402369, 1403744, 1403777, 1404224, 1404417, 1408096, 1408514, 1408832,
+ 1409025, 1766528, 1766913, 1767648, 1767777, 1769344, 2039809, 2051520, 2051585, 2054976, 2056193, 2056416,
+ 2056801, 2056960, 2057121, 2057152, 2057185, 2057504, 2057537, 2057952, 2057985, 2058144, 2058177, 2058208,
+ 2058241, 2058304, 2058337, 2058400, 2058433, 2061888, 2062945, 2074560, 2075137, 2077184, 2077249, 2078976,
+ 2080257, 2080640, 2084353, 2084512, 2084545, 2088864, 2089474, 2089792, 2090017, 2090848, 2091041, 2091872,
+ 2092225, 2095072, 2095169, 2095360, 2095425, 2095616, 2095681, 2095872, 2095937, 2096032, 2097153, 2097536,
+ 2097569, 2098400, 2098433, 2099040, 2099073, 2099136, 2099169, 2099648, 2099713, 2100160, 2101249, 2105184,
+ 2105571, 2107008, 2107395, 2109216, 2109763, 2109824, 2117633, 2118560, 2118657, 2120224, 2120739, 2121600,
+ 2121729, 2122755, 2122880, 2123169, 2123811, 2123841, 2124099, 2124128, 2124289, 2125504, 2125825, 2126784,
+ 2126849, 2128000, 2128129, 2128384, 2128419, 2128576, 2129921, 2134976, 2135042, 2135360, 2135553, 2136704,
+ 2136833, 2137984, 2138113, 2139392, 2139649, 2141312, 2141697, 2142048, 2142081, 2142560, 2142593, 2142816,
+ 2142849, 2142912, 2142945, 2143296, 2143329, 2143808, 2143841, 2144064, 2144097, 2144160, 2146305, 2156256,
+ 2156545, 2157248, 2157569, 2157824, 2158593, 2158784, 2158817, 2160160, 2160193, 2160480, 2162689, 2162880,
+ 2162945, 2162976, 2163009, 2164416, 2164449, 2164512, 2164609, 2164640, 2164705, 2165440, 2165507, 2165761,
+ 2166496, 2166563, 2166785, 2167776, 2168035, 2168320, 2169857, 2170464, 2170497, 2170560, 2170723, 2170881,
+ 2171587, 2171776, 2171905, 2172736, 2174977, 2176768, 2176899, 2176961, 2177027, 2177536, 2177603, 2179073,
+ 2179104, 2179585, 2179712, 2179745, 2179840, 2179873, 2180800, 2181123, 2181408, 2182145, 2183075, 2183136,
+ 2183169, 2184099, 2184192, 2185217, 2185472, 2185505, 2186400, 2186595, 2186752, 2187265, 2188992, 2189313,
+ 2190016, 2190083, 2190337, 2190944, 2191107, 2191361, 2191936, 2192675, 2192896, 2195457, 2197792, 2199553,
+ 2201184, 2201601, 2203232, 2203459, 2203649, 2204800, 2205186, 2205504, 2214915, 2215904, 2215937, 2217280,
+ 2217473, 2217536, 2220033, 2220963, 2221281, 2221312, 2221569, 2222272, 2222627, 2222752, 2223617, 2224192,
+ 2225665, 2226339, 2226560, 2227201, 2227936, 2228321, 2230016, 2230851, 2231490, 2231808, 2231841, 2231904,
+ 2231969, 2232000, 2232417, 2233856, 2234881, 2235680, 2235906, 2236224, 2236513, 2237664, 2238146, 2238464,
+ 2238593, 2238624, 2238689, 2238720, 2238977, 2240096, 2240193, 2240224, 2240609, 2242144, 2242593, 2242720,
+ 2243074, 2243393, 2243424, 2243457, 2243488, 2243619, 2244256, 2244609, 2245184, 2245217, 2246016, 2246625,
+ 2246688, 2248705, 2248928, 2248961, 2248992, 2249025, 2249152, 2249185, 2249664, 2249697, 2250016, 2250241,
+ 2251744, 2252290, 2252608, 2252961, 2253216, 2253281, 2253344, 2253409, 2254112, 2254145, 2254368, 2254401,
+ 2254464, 2254497, 2254656, 2254753, 2254784, 2255361, 2255392, 2255777, 2255936, 2260993, 2262688, 2263265,
+ 2263392, 2263554, 2263872, 2264033, 2264128, 2265089, 2266624, 2267265, 2267328, 2267361, 2267392, 2267650,
+ 2267968, 2273281, 2274784, 2276097, 2276224, 2277377, 2278912, 2279553, 2279584, 2279938, 2280256, 2281473,
+ 2282848, 2283265, 2283296, 2283522, 2283840, 2285569, 2286432, 2287106, 2287427, 2287488, 2287617, 2287840,
+ 2293761, 2295168, 2298881, 2300930, 2301251, 2301536, 2301921, 2302176, 2302241, 2302272, 2302337, 2302592,
+ 2302625, 2302688, 2302721, 2303488, 2303969, 2304000, 2304033, 2304064, 2304514, 2304832, 2307073, 2307328,
+ 2307393, 2308640, 2309153, 2309184, 2309217, 2309248, 2310145, 2310176, 2310497, 2311776, 2312001, 2312032,
+ 2312705, 2312736, 2313089, 2314560, 2315169, 2315200, 2315777, 2318112, 2326529, 2326816, 2326849, 2328032,
+ 2328577, 2328608, 2329090, 2329411, 2330016, 2330177, 2331136, 2334721, 2334944, 2334977, 2335040, 2335073,
+ 2336288, 2336961, 2336992, 2337282, 2337600, 2337793, 2337984, 2338017, 2338080, 2338113, 2339136, 2339585,
+ 2339616, 2339842, 2340160, 2350081, 2350688, 2351169, 2351200, 2351233, 2351648, 2351681, 2352768, 2353666,
+ 2353984, 2356737, 2356768, 2357251, 2357920, 2359297, 2388800, 2392067, 2395616, 2396161, 2402432, 2486785,
+ 2489888, 2490369, 2524672, 2525217, 2525408, 2654209, 2672864, 2949121, 2967328, 2967553, 2968544, 2968578,
+ 2968896, 2969089, 2971616, 2971650, 2971968, 2972161, 2973120, 2973697, 2975232, 2975745, 2975872, 2976258,
+ 2976576, 2976611, 2976832, 2976865, 2977536, 2977697, 2978304, 3000321, 3002371, 3003104, 3006465, 3008864,
+ 3009025, 3009056, 3011169, 3011584, 3013633, 3013696, 3013729, 3013760, 3014657, 3211008, 3211265, 3250880,
+ 3252225, 3252512, 3538433, 3538560, 3538593, 3538816, 3538849, 3538912, 3538945, 3548256, 3548737, 3548768,
+ 3549697, 3549792, 3549857, 3549888, 3550337, 3550464, 3550721, 3563392, 3637249, 3640672, 3640833, 3641248,
+ 3641345, 3641632, 3641857, 3642176, 3823619, 3824256, 3824643, 3825280, 3828739, 3829536, 3833857, 3836576,
+ 3836609, 3838880, 3838913, 3838976, 3839041, 3839072, 3839137, 3839200, 3839265, 3839392, 3839425, 3839808,
+ 3839841, 3839872, 3839905, 3840128, 3840161, 3842240, 3842273, 3842400, 3842465, 3842720, 3842753, 3842976,
+ 3843009, 3843904, 3843937, 3844064, 3844097, 3844256, 3844289, 3844320, 3844417, 3844640, 3844673, 3855552,
+ 3855617, 3856416, 3856449, 3857248, 3857281, 3858272, 3858305, 3859104, 3859137, 3860128, 3860161, 3860960,
+ 3860993, 3861984, 3862017, 3862816, 3862849, 3863840, 3863873, 3864672, 3864705, 3864960, 3865026, 3866624,
+ 3923969, 3924960, 3925153, 3925344, 3933697, 3935680, 3940353, 3941792, 3942113, 3942336, 3942402, 3942720,
+ 3942849, 3942880, 3953153, 3954112, 3954689, 3956096, 3956226, 3956544, 3971585, 3972480, 3972610, 3972928,
+ 3996673, 3996896, 3996929, 3997056, 3997089, 3997152, 3997185, 3997664, 3997697, 4004000, 4004067, 4004352,
+ 4005889, 4008064, 4008289, 4008320, 4008450, 4008768, 4034083, 4035968, 4036003, 4036096, 4036131, 4036256,
+ 4038691, 4040128, 4040163, 4040640, 4046849, 4046976, 4047009, 4047872, 4047905, 4047968, 4048001, 4048032,
+ 4048097, 4048128, 4048161, 4048480, 4048513, 4048640, 4048673, 4048704, 4048737, 4048768, 4048961, 4048992,
+ 4049121, 4049152, 4049185, 4049216, 4049249, 4049280, 4049313, 4049408, 4049441, 4049504, 4049537, 4049568,
+ 4049633, 4049664, 4049697, 4049728, 4049761, 4049792, 4049825, 4049856, 4049889, 4049920, 4049953, 4050016,
+ 4050049, 4050080, 4050145, 4050272, 4050305, 4050528, 4050561, 4050688, 4050721, 4050848, 4050881, 4050912,
+ 4050945, 4051264, 4051297, 4051840, 4052001, 4052096, 4052129, 4052288, 4052321, 4052864, 4071427, 4071840,
+ 4161026, 4161344, 4194305, 5561344, 5562369, 5695296, 5695489, 5702592, 5702657, 5887040, 5887489, 6126624,
+ 6225921, 6243264, 6291457, 6449504, 6449665, 6583808, 4294967295};
+
+static const uint16 unicode_simple_category_jump_pos[] = {
+ 1, 9, 27, 27, 27, 27, 36, 44, 55, 55, 57, 63, 68, 75, 86, 91, 102, 114, 119,
+ 130, 158, 180, 202, 225, 250, 271, 292, 312, 324, 332, 357, 365, 368, 383, 397, 397, 397, 407,
+ 423, 431, 436, 437, 437, 437, 437, 440, 448, 458, 467, 472, 480, 487, 494, 498, 503, 509, 516,
+ 524, 538, 538, 540, 540, 540, 558, 578, 592, 595, 622, 625, 625, 625, 625, 625, 626, 629, 629,
+ 629, 629, 629, 630, 631, 631, 631, 631, 631, 631, 631, 631, 632, 632, 640, 650, 667, 669, 669,
+ 669, 670, 682, 689, 692, 699, 706, 709, 709, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710,
+ 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710,
+ 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710,
+ 710, 710, 710, 710, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712, 712,
+ 712, 712, 712, 712, 712, 712, 712, 716, 716, 716, 724, 728, 731, 741, 752, 763, 769, 781, 793,
+ 810, 825, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829,
+ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829,
+ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829,
+ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829,
+ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 829, 834, 834, 834, 834, 834,
+ 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834,
+ 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834,
+ 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834, 834,
+ 834, 834, 834, 834, 835, 835, 835, 837, 839, 857, 859, 859, 859, 861, 866, 869, 870, 877, 887,
+ 899, 900, 904, 906, 907, 913, 923, 931, 931, 939, 945, 959, 959, 959, 965, 971, 987, 996, 1001,
+ 1008, 1021, 1030, 1038, 1042, 1044, 1049, 1052, 1052, 1055, 1059, 1067, 1073, 1082, 1088, 1100, 1112, 1118, 1131,
+ 1149, 1150, 1158, 1165, 1166, 1170, 1176, 1182, 1188, 1189, 1190, 1195, 1210, 1219, 1227, 1232, 1232, 1233, 1242,
+ 1244, 1258, 1263, 1263, 1265, 1273, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1280, 1282, 1282, 1283, 1283,
+ 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283,
+ 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289,
+ 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289,
+ 1289, 1289, 1290, 1290, 1290, 1290, 1290, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291,
+ 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291,
+ 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291,
+ 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1292, 1292,
+ 1292, 1292, 1292, 1298, 1304, 1314, 1315, 1315, 1315, 1315, 1315, 1317, 1319, 1322, 1329, 1329, 1329, 1329, 1329,
+ 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329,
+ 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329,
+ 1329, 1329, 1329, 1329, 1329, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1333, 1334, 1334, 1334,
+ 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334,
+ 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334,
+ 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334,
+ 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1341, 1341, 1341, 1351, 1351, 1351, 1352, 1352, 1352, 1352,
+ 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1352, 1353, 1357, 1360, 1360, 1360,
+ 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360,
+ 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360,
+ 1360, 1360, 1360, 1364, 1366, 1367, 1369, 1385, 1403, 1403, 1403, 1411, 1419, 1428, 1428, 1428, 1428, 1428, 1428,
+ 1428, 1428, 1428, 1428, 1428, 1428, 1428, 1428, 1429, 1432, 1432, 1434, 1435, 1442, 1442, 1442, 1448, 1448, 1448,
+ 1448, 1452, 1452, 1452, 1452, 1452, 1452, 1461, 1461, 1465, 1470, 1470, 1470, 1470, 1470, 1470, 1471, 1476, 1480,
+ 1481, 1537, 1546, 1546, 1546, 1546, 1547, 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
+ 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1550, 1551, 1563,
+ 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566};
+
+static const char *unicode_simple_category_table =
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x02\x02\x02\x02\x02\x02"
+ "\x02\x02\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00";
static constexpr uint32 TABLE_SIZE = 1280;
-static int16 prepare_search_character_table[TABLE_SIZE] = {
+static const int16 prepare_search_character_table[TABLE_SIZE] = {
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 48, 49, 50, 51, 52, 53, 54, 55, 56,
@@ -202,219 +281,252 @@ static int16 prepare_search_character_table[TABLE_SIZE] = {
1273, 1275, 1275, 1277, 1277, 1279, 1279};
static const int32 prepare_search_character_ranges[] = {
- 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 1370, 32, 1376, -1377,
- 1417, 32, 1419, -1420, 1421, 32, 1424, 1424, 1425, 0, 1470, 32,
- 1471, 0, 1472, 32, 1473, 0, 1475, 32, 1476, 0, 1478, 32,
- 1479, 0, 1480, -1481, 1523, 32, 1525, -1526, 1536, 0, 1542, 32,
- 1552, 0, 1563, 32, 1564, 0, 1565, 1565, 1566, 32, 1568, -1569,
- 1611, 0, 1632, -1633, 1642, 32, 1646, -1647, 1648, 0, 1649, -1650,
- 1748, 32, 1749, 1749, 1750, 0, 1758, 32, 1759, 0, 1765, -1766,
- 1767, 0, 1769, 32, 1770, 0, 1774, -1775, 1789, 32, 1791, 1791,
- 1792, 32, 1806, 1806, 1807, 0, 1808, 1808, 1809, 0, 1810, -1811,
- 1840, 0, 1867, -1868, 1958, 0, 1969, -1970, 2027, 0, 2036, -2037,
- 2038, 32, 2042, -2043, 2070, 0, 2074, 2074, 2075, 0, 2084, 2084,
- 2085, 0, 2088, 2088, 2089, 0, 2094, -2095, 2096, 32, 2111, -2112,
- 2137, 0, 2140, -2141, 2142, 32, 2143, -2144, 2260, 0, 2308, -2309,
- 2362, 0, 2365, 2365, 2366, 0, 2384, 2384, 2385, 0, 2392, -2393,
- 2402, 0, 2404, 32, 2406, -2407, 2416, 32, 2417, -2418, 2433, 0,
- 2436, -2437, 2492, 0, 2493, 2493, 2494, 0, 2501, -2502, 2503, 0,
- 2505, -2506, 2507, 0, 2510, -2511, 2519, 0, 2520, -2521, 2530, 0,
- 2532, -2533, 2546, 32, 2548, -2549, 2554, 32, 2556, -2557, 2561, 0,
- 2564, -2565, 2620, 0, 2621, 2621, 2622, 0, 2627, -2628, 2631, 0,
- 2633, -2634, 2635, 0, 2638, -2639, 2641, 0, 2642, -2643, 2672, 0,
- 2674, -2675, 2677, 0, 2678, -2679, 2689, 0, 2692, -2693, 2748, 0,
- 2749, 2749, 2750, 0, 2758, 2758, 2759, 0, 2762, 2762, 2763, 0,
- 2766, -2767, 2786, 0, 2788, -2789, 2800, 32, 2802, -2803, 2817, 0,
- 2820, -2821, 2876, 0, 2877, 2877, 2878, 0, 2885, -2886, 2887, 0,
- 2889, -2890, 2891, 0, 2894, -2895, 2902, 0, 2904, -2905, 2914, 0,
- 2916, -2917, 2928, 32, 2929, -2930, 2946, 0, 2947, -2948, 3006, 0,
- 3011, -3012, 3014, 0, 3017, 3017, 3018, 0, 3022, -3023, 3031, 0,
- 3032, -3033, 3059, 32, 3067, -3068, 3072, 0, 3076, -3077, 3134, 0,
- 3141, 3141, 3142, 0, 3145, 3145, 3146, 0, 3150, -3151, 3157, 0,
- 3159, -3160, 3170, 0, 3172, -3173, 3199, 32, 3200, 3200, 3201, 0,
- 3204, -3205, 3260, 0, 3261, 3261, 3262, 0, 3269, 3269, 3270, 0,
- 3273, 3273, 3274, 0, 3278, -3279, 3285, 0, 3287, -3288, 3298, 0,
- 3300, -3301, 3329, 0, 3332, -3333, 3390, 0, 3397, 3397, 3398, 0,
- 3401, 3401, 3402, 0, 3406, 3406, 3407, 32, 3408, -3409, 3415, 0,
- 3416, -3417, 3426, 0, 3428, -3429, 3449, 32, 3450, -3451, 3458, 0,
- 3460, -3461, 3530, 0, 3531, -3532, 3535, 0, 3541, 3541, 3542, 0,
- 3543, 3543, 3544, 0, 3552, -3553, 3570, 0, 3572, 32, 3573, -3574,
- 3633, 0, 3634, -3635, 3636, 0, 3643, -3644, 3647, 32, 3648, -3649,
- 3655, 0, 3663, 32, 3664, -3665, 3674, 32, 3676, -3677, 3761, 0,
- 3762, -3763, 3764, 0, 3770, 3770, 3771, 0, 3773, -3774, 3784, 0,
- 3790, -3791, 3841, 32, 3864, 0, 3866, 32, 3872, -3873, 3892, 32,
- 3893, 0, 3894, 32, 3895, 0, 3896, 32, 3897, 0, 3898, 32,
- 3902, 0, 3904, -3905, 3953, 0, 3973, 32, 3974, 0, 3976, -3977,
- 3981, 0, 3992, 3992, 3993, 0, 4029, 4029, 4030, 32, 4038, 0,
- 4039, 32, 4045, 4045, 4046, 32, 4059, -4060, 4139, 0, 4159, -4160,
- 4170, 32, 4176, -4177, 4182, 0, 4186, -4187, 4190, 0, 4193, 4193,
- 4194, 0, 4197, -4198, 4199, 0, 4206, -4207, 4209, 0, 4213, -4214,
- 4226, 0, 4238, 4238, 4239, 0, 4240, -4241, 4250, 0, 4254, 32,
- 4256, -11521, 4294, 4294, 4295, 11559, 4296, -4297, 4301, 11565, 4302, -4303,
- 4347, 32, 4348, -4349, 4957, 0, 4960, 32, 4969, -4970, 5008, 32,
- 5018, -5019, 5112, -5105, 5118, -5119, 5120, 32, 5121, -5122, 5741, 32,
- 5743, -5744, 5760, 32, 5761, -5762, 5787, 32, 5789, -5790, 5867, 32,
- 5870, -5871, 5906, 0, 5909, -5910, 5938, 0, 5941, 32, 5943, -5944,
- 5970, 0, 5972, -5973, 6002, 0, 6004, -6005, 6068, 0, 6100, 32,
- 6103, 6103, 6104, 32, 6108, 6108, 6109, 0, 6110, -6111, 6144, 32,
- 6155, 0, 6159, -6160, 6277, 0, 6279, -6280, 6313, 0, 6314, -6315,
- 6432, 0, 6444, -6445, 6448, 0, 6460, -6461, 6464, 32, 6465, -6466,
- 6468, 32, 6470, -6471, 6622, 32, 6656, -6657, 6679, 0, 6684, -6685,
- 6686, 32, 6688, -6689, 6741, 0, 6751, 6751, 6752, 0, 6781, -6782,
- 6783, 0, 6784, -6785, 6816, 32, 6823, 6823, 6824, 32, 6830, -6831,
- 6832, 0, 6847, -6848, 6912, 0, 6917, -6918, 6964, 0, 6981, -6982,
- 7002, 32, 7019, 0, 7028, 32, 7037, -7038, 7040, 0, 7043, -7044,
- 7073, 0, 7086, -7087, 7142, 0, 7156, -7157, 7164, 32, 7168, -7169,
- 7204, 0, 7224, -7225, 7227, 32, 7232, -7233, 7294, 32, 7296, 1074,
- 7297, 1076, 7298, 1086, 7299, -1090, 7301, 1090, 7302, 1098, 7303, 1123,
- 7304, 42571, 7305, -7306, 7360, 32, 7368, -7369, 7376, 0, 7379, 32,
- 7380, 0, 7401, -7402, 7405, 0, 7406, -7407, 7410, 0, 7413, -7414,
- 7416, 0, 7418, -7419, 7468, 97, 7469, 230, 7470, 98, 7471, 7471,
- 7472, -101, 7474, 477, 7475, -104, 7483, 7483, 7484, 111, 7485, 547,
- 7486, 112, 7487, 114, 7488, -117, 7490, 119, 7491, -7492, 7616, 0,
- 7670, -7671, 7675, 0, 7680, 2097153, 7830, -7831, 7835, 7777, 7836, -7837,
- 7838, 223, 7839, 2097153, 7936, -7937, 7944, -7937, 7952, -7953, 7960, -7953,
- 7966, -7967, 7976, -7969, 7984, -7985, 7992, -7985, 8000, -8001, 8008, -8001,
- 8014, -8015, 8025, 8017, 8026, 8026, 8027, 8019, 8028, 8028, 8029, 8021,
- 8030, 8030, 8031, 8023, 8032, -8033, 8040, -8033, 8048, -8049, 8072, -8065,
- 8080, -8081, 8088, -8081, 8096, -8097, 8104, -8097, 8112, -8113, 8120, -8113,
- 8122, -8049, 8124, 8115, 8125, 32, 8126, 953, 8127, 32, 8130, -8131,
- 8136, -8051, 8140, 8131, 8141, 32, 8144, -8145, 8152, -8145, 8154, -8055,
- 8156, 8156, 8157, 32, 8160, -8161, 8168, -8161, 8170, -8059, 8172, 8165,
- 8173, 32, 8176, -8177, 8184, -8057, 8186, -8061, 8188, 8179, 8189, 32,
- 8191, 8191, 8192, 32, 8203, 0, 8208, 32, 8234, 0, 8239, 32,
- 8288, 0, 8293, 8293, 8294, 0, 8304, -8305, 8314, 32, 8319, -8320,
- 8330, 32, 8335, -8336, 8352, 32, 8383, -8384, 8400, 0, 8433, -8434,
- 8448, 32, 8450, 99, 8452, 32, 8455, 603, 8456, 32, 8457, 102,
- 8458, 8458, 8459, 104, 8462, -8463, 8464, 105, 8466, 108, 8467, 8467,
- 8468, 32, 8469, 110, 8470, 32, 8473, -113, 8476, 114, 8478, 32,
- 8484, 122, 8485, 32, 8486, 969, 8487, 32, 8488, 122, 8489, 32,
- 8490, 107, 8491, 229, 8492, -99, 8494, 32, 8495, 8495, 8496, -102,
- 8498, 8526, 8499, 109, 8500, -8501, 8506, 32, 8508, -8509, 8510, 947,
- 8511, 960, 8512, 32, 8517, 100, 8518, -8519, 8522, 32, 8526, 8526,
- 8527, 32, 8528, -8529, 8544, -8561, 8560, -8561, 8579, 8580, 8581, -8582,
- 8586, 32, 8588, -8589, 8592, 32, 9215, 9215, 9216, 32, 9255, -9256,
- 9280, 32, 9291, -9292, 9372, 32, 9398, -9425, 9424, -9425, 9472, 32,
- 10102, -10103, 10132, 32, 11124, -11125, 11126, 32, 11158, -11159, 11160, 32,
- 11194, -11195, 11197, 32, 11209, 11209, 11210, 32, 11218, -11219, 11244, 32,
- 11248, -11249, 11264, -11313, 11311, -11312, 11360, 11361, 11362, 619, 11363, 7549,
- 11364, 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 593,
- 11374, 625, 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380, 11381, 11382,
- 11383, -11384, 11389, 118, 11390, -576, 11392, 2097153, 11492, 11492, 11493, 32,
- 11499, 11500, 11501, 11502, 11503, 0, 11506, 11507, 11508, -11509, 11513, 32,
- 11517, 11517, 11518, 32, 11520, -11521, 11632, 32, 11633, -11634, 11647, 0,
- 11648, -11649, 11744, 0, 11776, 32, 11823, 11823, 11824, 32, 11845, -11846,
- 11904, 32, 11930, 11930, 11931, 32, 11935, 11935, 11936, 32, 12019, -12020,
- 12272, 32, 12284, -12285, 12288, 32, 12293, -12294, 12296, 32, 12321, -12322,
- 12330, 0, 12336, 32, 12337, -12338, 12342, 32, 12344, -12345, 12349, 32,
- 12352, -12353, 12441, 0, 12443, 32, 12445, -12446, 12448, 32, 12449, -12450,
- 12539, 32, 12540, 0, 12541, -12542, 12688, 32, 12690, -12691, 12736, 32,
- 12772, -12773, 12800, 32, 12831, -12832, 12842, 32, 12868, -12869, 12880, 32,
- 12881, -12882, 12910, 32, 12928, -12929, 12992, 32, 13008, -13009, 13056, 32,
- 13312, -13313, 19904, 32, 19968, -19969, 42128, 32, 42183, -42184, 42238, 32,
- 42240, -42241, 42509, 32, 42512, -42513, 42560, 2097153, 42606, 42606, 42607, 0,
- 42611, 32, 42612, 0, 42622, 32, 42623, 2097153, 42652, -42653, 42654, 0,
- 42656, -42657, 42736, 0, 42738, 32, 42744, -42745, 42752, 32, 42775, -42776,
- 42784, 32, 42786, 2097153, 42800, -42801, 42802, 2097153, 42864, -42865, 42873, 42874,
- 42875, 42876, 42877, 7545, 42878, 2097153, 42888, 42888, 42889, 32, 42891, 42892,
- 42893, 613, 42894, -42895, 42896, 2097153, 42900, -42901, 42902, 2097153, 42922, 614,
- 42923, 604, 42924, 609, 42925, 620, 42926, 618, 42927, 42927, 42928, 670,
- 42929, 647, 42930, 669, 42931, 43859, 42932, 2097153, 42936, -42937, 43000, 295,
- 43001, -43002, 43010, 0, 43011, -43012, 43014, 0, 43015, -43016, 43019, 0,
- 43020, -43021, 43043, 0, 43048, 32, 43052, -43053, 43062, 32, 43066, -43067,
- 43124, 32, 43128, -43129, 43136, 0, 43138, -43139, 43188, 0, 43206, -43207,
- 43214, 32, 43216, -43217, 43232, 0, 43250, -43251, 43256, 32, 43259, 43259,
- 43260, 32, 43261, -43262, 43302, 0, 43310, 32, 43312, -43313, 43335, 0,
- 43348, -43349, 43359, 32, 43360, -43361, 43392, 0, 43396, -43397, 43443, 0,
- 43457, 32, 43470, -43471, 43486, 32, 43488, -43489, 43493, 0, 43494, -43495,
- 43561, 0, 43575, -43576, 43587, 0, 43588, -43589, 43596, 0, 43598, -43599,
- 43612, 32, 43616, -43617, 43639, 32, 43642, 43642, 43643, 0, 43646, -43647,
- 43696, 0, 43697, 43697, 43698, 0, 43701, -43702, 43703, 0, 43705, -43706,
- 43710, 0, 43712, 43712, 43713, 0, 43714, -43715, 43742, 32, 43744, -43745,
- 43755, 0, 43760, 32, 43762, -43763, 43765, 0, 43767, -43768, 43867, 32,
- 43868, -43869, 43888, -5025, 43968, -43969, 44003, 0, 44011, 32, 44012, 0,
- 44014, -44015, 55296, 0, 57344, -57345, 64286, 0, 64287, -64288, 64297, 32,
- 64298, -64299, 64434, 32, 64450, -64451, 64830, 32, 64832, -64833, 64976, 32,
- 65008, -65009, 65020, 32, 65022, -65023, 65024, 0, 65040, 32, 65050, -65051,
- 65056, 0, 65072, 32, 65107, 65107, 65108, 32, 65127, 65127, 65128, 32,
- 65132, -65133, 65279, 0, 65280, 65280, 65281, 32, 65296, -65297, 65306, 32,
- 65313, -65346, 65339, 32, 65345, -65346, 65371, 32, 65382, -65383, 65504, 32,
- 65511, 65511, 65512, 32, 65519, -65520, 65529, 0, 65532, 32, 65536, -65537,
- 65792, 32, 65795, -65796, 65847, 32, 65856, -65857, 65913, 32, 65930, -65931,
- 65932, 32, 65935, 65935, 65936, 32, 65948, -65949, 65952, 32, 65953, -65954,
- 66000, 32, 66045, 0, 66046, -66047, 66272, 0, 66273, -66274, 66422, 0,
- 66427, -66428, 66463, 32, 66464, -66465, 66512, 32, 66513, -66514, 66560, -66601,
- 66600, -66601, 66736, -66777, 66772, -66773, 66927, 32, 66928, -66929, 67671, 32,
- 67672, -67673, 67703, 32, 67705, -67706, 67871, 32, 67872, -67873, 67903, 32,
- 67904, -67905, 68097, 0, 68100, 68100, 68101, 0, 68103, -68104, 68108, 0,
- 68112, -68113, 68152, 0, 68155, -68156, 68159, 0, 68160, -68161, 68176, 32,
- 68185, -68186, 68223, 32, 68224, -68225, 68296, 32, 68297, -68298, 68325, 0,
- 68327, -68328, 68336, 32, 68343, -68344, 68409, 32, 68416, -68417, 68505, 32,
- 68509, -68510, 68736, -68801, 68787, -68788, 69632, 0, 69635, -69636, 69688, 0,
- 69703, 32, 69710, -69711, 69759, 0, 69763, -69764, 69808, 0, 69819, 32,
- 69821, 0, 69822, 32, 69826, -69827, 69888, 0, 69891, -69892, 69927, 0,
- 69941, -69942, 69952, 32, 69956, -69957, 70003, 0, 70004, 32, 70006, -70007,
- 70016, 0, 70019, -70020, 70067, 0, 70081, -70082, 70085, 32, 70090, 0,
- 70093, 32, 70094, -70095, 70107, 32, 70108, 70108, 70109, 32, 70112, -70113,
- 70188, 0, 70200, 32, 70206, 0, 70207, -70208, 70313, 32, 70314, -70315,
- 70367, 0, 70379, -70380, 70400, 0, 70404, -70405, 70460, 0, 70461, 70461,
- 70462, 0, 70469, -70470, 70471, 0, 70473, -70474, 70475, 0, 70478, -70479,
- 70487, 0, 70488, -70489, 70498, 0, 70500, -70501, 70502, 0, 70509, -70510,
- 70512, 0, 70517, -70518, 70709, 0, 70727, -70728, 70731, 32, 70736, -70737,
- 70747, 32, 70748, 70748, 70749, 32, 70750, -70751, 70832, 0, 70852, -70853,
- 70854, 32, 70855, -70856, 71087, 0, 71094, -71095, 71096, 0, 71105, 32,
- 71128, -71129, 71132, 0, 71134, -71135, 71216, 0, 71233, 32, 71236, -71237,
- 71264, 32, 71277, -71278, 71339, 0, 71352, -71353, 71453, 0, 71468, -71469,
- 71484, 32, 71488, -71489, 71840, -71873, 71872, -71873, 72751, 0, 72759, 72759,
- 72760, 0, 72768, 72768, 72769, 32, 72774, -72775, 72816, 32, 72818, -72819,
- 72850, 0, 72872, 72872, 72873, 0, 72887, -72888, 74864, 32, 74869, -74870,
- 92782, 32, 92784, -92785, 92912, 0, 92917, 32, 92918, -92919, 92976, 0,
- 92983, 32, 92992, -92993, 92996, 32, 92998, -92999, 94033, 0, 94079, -94080,
- 94095, 0, 94099, -94100, 113820, 32, 113821, 0, 113823, 32, 113824, 0,
- 113828, -113829, 118784, 32, 119030, -119031, 119040, 32, 119079, -119080, 119081, 32,
- 119141, 0, 119146, 32, 119149, 0, 119171, 32, 119173, 0, 119180, 32,
- 119210, 0, 119214, 32, 119273, -119274, 119296, 32, 119362, 0, 119365, 32,
- 119366, -119367, 119552, 32, 119639, -119640, 119808, -98, 119834, -119835, 119860, -98,
- 119886, -119887, 119912, -98, 119938, -119939, 119964, 97, 119965, 119965, 119966, -100,
- 119968, -119969, 119970, 103, 119971, -119972, 119973, -107, 119975, -119976, 119977, -111,
- 119981, 119981, 119982, -116, 119990, -119991, 120016, -98, 120042, -120043, 120068, -98,
- 120070, 120070, 120071, -101, 120075, -120076, 120077, -107, 120085, 120085, 120086, -116,
- 120093, -120094, 120120, -98, 120122, 120122, 120123, -101, 120127, 120127, 120128, -106,
- 120133, 120133, 120134, 111, 120135, -120136, 120138, -116, 120145, -120146, 120172, -98,
- 120198, -120199, 120224, -98, 120250, -120251, 120276, -98, 120302, -120303, 120328, -98,
- 120354, -120355, 120380, -98, 120406, -120407, 120432, -98, 120458, -120459, 120488, -946,
- 120505, 952, 120506, -964, 120513, 32, 120514, -120515, 120531, 963, 120532, -120533,
- 120539, 32, 120540, -120541, 120546, -946, 120563, 952, 120564, -964, 120571, 32,
- 120572, -120573, 120589, 963, 120590, -120591, 120597, 32, 120598, -120599, 120604, -946,
- 120621, 952, 120622, -964, 120629, 32, 120630, -120631, 120647, 963, 120648, -120649,
- 120655, 32, 120656, -120657, 120662, -946, 120679, 952, 120680, -964, 120687, 32,
- 120688, -120689, 120705, 963, 120706, -120707, 120713, 32, 120714, -120715, 120720, -946,
- 120737, 952, 120738, -964, 120745, 32, 120746, -120747, 120763, 963, 120764, -120765,
- 120771, 32, 120772, -120773, 120778, 989, 120779, -120780, 120832, 32, 121344, 0,
- 121399, 32, 121403, 0, 121453, 32, 121461, 0, 121462, 32, 121476, 0,
- 121477, 32, 121484, -121485, 121499, 0, 121504, 121504, 121505, 0, 121520, -121521,
- 122880, 0, 122887, 122887, 122888, 0, 122905, -122906, 122907, 0, 122914, 122914,
- 122915, 0, 122917, 122917, 122918, 0, 122923, -122924, 125136, 0, 125143, -125144,
- 125184, -125219, 125218, -125219, 125252, 0, 125259, -125260, 125278, 32, 125280, -125281,
- 126704, 32, 126706, -126707, 126976, 32, 127020, -127021, 127024, 32, 127124, -127125,
- 127136, 32, 127151, -127152, 127153, 32, 127168, 127168, 127169, 32, 127184, 127184,
- 127185, 32, 127222, -127223, 127248, 32, 127275, 99, 127276, 114, 127277, 32,
- 127279, 127279, 127280, -98, 127306, 32, 127340, -127341, 127344, 32, 127405, -127406,
- 127462, 32, 127490, -127491, 127552, 32, 127561, -127562, 127744, 32, 128723, -128724,
- 128736, 32, 128749, -128750, 128752, 32, 128759, -128760, 128768, 32, 128884, -128885,
- 128896, 32, 128981, -128982, 129024, 32, 129036, -129037, 129040, 32, 129096, -129097,
- 129104, 32, 129114, -129115, 129120, 32, 129160, -129161, 129168, 32, 129198, -129199,
- 129296, 32, 129311, 129311, 129312, 32, 129320, -129321, 129328, 32, 129329, -129330,
- 129331, 32, 129343, 129343, 129344, 32, 129356, -129357, 129360, 32, 129375, -129376,
- 129408, 32, 129426, -129427, 129472, 32, 129473, -129474, 131070, 32, 131072, -131073,
- 196606, 32, 196608, -196609, 262142, 32, 262144, -262145, 327678, 32, 327680, -327681,
- 393214, 32, 393216, -393217, 458750, 32, 458752, -458753, 524286, 32, 524288, -524289,
- 589822, 32, 589824, -589825, 655358, 32, 655360, -655361, 720894, 32, 720896, -720897,
- 786430, 32, 786432, -786433, 851966, 32, 851968, -851969, 917502, 32, 917504, 917504,
- 917505, 0, 917506, -917507, 917536, 0, 917632, -917633, 917760, 0, 918000, -918001,
- 983038, 32, 983040, -983041, 1048574, 32, 1048576, -1048577, 1114110, 32, 2147483647, 0};
+ 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 1370, 32, 1376, -1377,
+ 1417, 32, 1419, -1420, 1421, 32, 1424, 1424, 1425, 0, 1470, 32,
+ 1471, 0, 1472, 32, 1473, 0, 1475, 32, 1476, 0, 1478, 32,
+ 1479, 0, 1480, -1481, 1523, 32, 1525, -1526, 1536, 0, 1542, 32,
+ 1552, 0, 1563, 32, 1564, 0, 1565, 32, 1568, -1569, 1611, 0,
+ 1632, -1633, 1642, 32, 1646, -1647, 1648, 0, 1649, -1650, 1748, 32,
+ 1749, 1749, 1750, 0, 1758, 32, 1759, 0, 1765, -1766, 1767, 0,
+ 1769, 32, 1770, 0, 1774, -1775, 1789, 32, 1791, 1791, 1792, 32,
+ 1806, 1806, 1807, 0, 1808, 1808, 1809, 0, 1810, -1811, 1840, 0,
+ 1867, -1868, 1958, 0, 1969, -1970, 2027, 0, 2036, -2037, 2038, 32,
+ 2042, -2043, 2045, 0, 2046, 32, 2048, -2049, 2070, 0, 2074, 2074,
+ 2075, 0, 2084, 2084, 2085, 0, 2088, 2088, 2089, 0, 2094, -2095,
+ 2096, 32, 2111, -2112, 2137, 0, 2140, -2141, 2142, 32, 2143, -2144,
+ 2184, 32, 2185, -2186, 2192, 0, 2194, -2195, 2200, 0, 2208, -2209,
+ 2250, 0, 2308, -2309, 2362, 0, 2365, 2365, 2366, 0, 2384, 2384,
+ 2385, 0, 2392, -2393, 2402, 0, 2404, 32, 2406, -2407, 2416, 32,
+ 2417, -2418, 2433, 0, 2436, -2437, 2492, 0, 2493, 2493, 2494, 0,
+ 2501, -2502, 2503, 0, 2505, -2506, 2507, 0, 2510, -2511, 2519, 0,
+ 2520, -2521, 2530, 0, 2532, -2533, 2546, 32, 2548, -2549, 2554, 32,
+ 2556, 2556, 2557, 32, 2558, 0, 2559, -2560, 2561, 0, 2564, -2565,
+ 2620, 0, 2621, 2621, 2622, 0, 2627, -2628, 2631, 0, 2633, -2634,
+ 2635, 0, 2638, -2639, 2641, 0, 2642, -2643, 2672, 0, 2674, -2675,
+ 2677, 0, 2678, 32, 2679, -2680, 2689, 0, 2692, -2693, 2748, 0,
+ 2749, 2749, 2750, 0, 2758, 2758, 2759, 0, 2762, 2762, 2763, 0,
+ 2766, -2767, 2786, 0, 2788, -2789, 2800, 32, 2802, -2803, 2810, 0,
+ 2816, 2816, 2817, 0, 2820, -2821, 2876, 0, 2877, 2877, 2878, 0,
+ 2885, -2886, 2887, 0, 2889, -2890, 2891, 0, 2894, -2895, 2901, 0,
+ 2904, -2905, 2914, 0, 2916, -2917, 2928, 32, 2929, -2930, 2946, 0,
+ 2947, -2948, 3006, 0, 3011, -3012, 3014, 0, 3017, 3017, 3018, 0,
+ 3022, -3023, 3031, 0, 3032, -3033, 3059, 32, 3067, -3068, 3072, 0,
+ 3077, -3078, 3132, 0, 3133, 3133, 3134, 0, 3141, 3141, 3142, 0,
+ 3145, 3145, 3146, 0, 3150, -3151, 3157, 0, 3159, -3160, 3170, 0,
+ 3172, -3173, 3191, 32, 3192, -3193, 3199, 32, 3200, 3200, 3201, 0,
+ 3204, 32, 3205, -3206, 3260, 0, 3261, 3261, 3262, 0, 3269, 3269,
+ 3270, 0, 3273, 3273, 3274, 0, 3278, -3279, 3285, 0, 3287, -3288,
+ 3298, 0, 3300, -3301, 3315, 0, 3316, -3317, 3328, 0, 3332, -3333,
+ 3387, 0, 3389, 3389, 3390, 0, 3397, 3397, 3398, 0, 3401, 3401,
+ 3402, 0, 3406, 3406, 3407, 32, 3408, -3409, 3415, 0, 3416, -3417,
+ 3426, 0, 3428, -3429, 3449, 32, 3450, -3451, 3457, 0, 3460, -3461,
+ 3530, 0, 3531, -3532, 3535, 0, 3541, 3541, 3542, 0, 3543, 3543,
+ 3544, 0, 3552, -3553, 3570, 0, 3572, 32, 3573, -3574, 3633, 0,
+ 3634, -3635, 3636, 0, 3643, -3644, 3647, 32, 3648, -3649, 3655, 0,
+ 3663, 32, 3664, -3665, 3674, 32, 3676, -3677, 3761, 0, 3762, -3763,
+ 3764, 0, 3773, -3774, 3784, 0, 3791, -3792, 3841, 32, 3864, 0,
+ 3866, 32, 3872, -3873, 3892, 32, 3893, 0, 3894, 32, 3895, 0,
+ 3896, 32, 3897, 0, 3898, 32, 3902, 0, 3904, -3905, 3953, 0,
+ 3973, 32, 3974, 0, 3976, -3977, 3981, 0, 3992, 3992, 3993, 0,
+ 4029, 4029, 4030, 32, 4038, 0, 4039, 32, 4045, 4045, 4046, 32,
+ 4059, -4060, 4139, 0, 4159, -4160, 4170, 32, 4176, -4177, 4182, 0,
+ 4186, -4187, 4190, 0, 4193, 4193, 4194, 0, 4197, -4198, 4199, 0,
+ 4206, -4207, 4209, 0, 4213, -4214, 4226, 0, 4238, 4238, 4239, 0,
+ 4240, -4241, 4250, 0, 4254, 32, 4256, -11521, 4294, 4294, 4295, 11559,
+ 4296, -4297, 4301, 11565, 4302, -4303, 4347, 32, 4348, -4349, 4957, 0,
+ 4960, 32, 4969, -4970, 5008, 32, 5018, -5019, 5112, -5105, 5118, -5119,
+ 5120, 32, 5121, -5122, 5741, 32, 5743, -5744, 5760, 32, 5761, -5762,
+ 5787, 32, 5789, -5790, 5867, 32, 5870, -5871, 5906, 0, 5910, -5911,
+ 5938, 0, 5941, 32, 5943, -5944, 5970, 0, 5972, -5973, 6002, 0,
+ 6004, -6005, 6068, 0, 6100, 32, 6103, 6103, 6104, 32, 6108, 6108,
+ 6109, 0, 6110, -6111, 6144, 32, 6155, 0, 6160, -6161, 6277, 0,
+ 6279, -6280, 6313, 0, 6314, -6315, 6432, 0, 6444, -6445, 6448, 0,
+ 6460, -6461, 6464, 32, 6465, -6466, 6468, 32, 6470, -6471, 6622, 32,
+ 6656, -6657, 6679, 0, 6684, -6685, 6686, 32, 6688, -6689, 6741, 0,
+ 6751, 6751, 6752, 0, 6781, -6782, 6783, 0, 6784, -6785, 6816, 32,
+ 6823, 6823, 6824, 32, 6830, -6831, 6832, 0, 6863, -6864, 6912, 0,
+ 6917, -6918, 6964, 0, 6981, -6982, 7002, 32, 7019, 0, 7028, 32,
+ 7039, 7039, 7040, 0, 7043, -7044, 7073, 0, 7086, -7087, 7142, 0,
+ 7156, -7157, 7164, 32, 7168, -7169, 7204, 0, 7224, -7225, 7227, 32,
+ 7232, -7233, 7294, 32, 7296, 1074, 7297, 1076, 7298, 1086, 7299, -1090,
+ 7301, 1090, 7302, 1098, 7303, 1123, 7304, 42571, 7305, -7306, 7312, -4305,
+ 7355, -7356, 7357, -4350, 7360, 32, 7368, -7369, 7376, 0, 7379, 32,
+ 7380, 0, 7401, -7402, 7405, 0, 7406, -7407, 7412, 0, 7413, -7414,
+ 7415, 0, 7418, -7419, 7468, 97, 7469, 230, 7470, 98, 7471, 7471,
+ 7472, -101, 7474, 477, 7475, -104, 7483, 7483, 7484, 111, 7485, 547,
+ 7486, 112, 7487, 114, 7488, -117, 7490, 119, 7491, -7492, 7616, 0,
+ 7680, 2097153, 7830, -7831, 7835, 7777, 7836, -7837, 7838, 223, 7839, 2097153,
+ 7936, -7937, 7944, -7937, 7952, -7953, 7960, -7953, 7966, -7967, 7976, -7969,
+ 7984, -7985, 7992, -7985, 8000, -8001, 8008, -8001, 8014, -8015, 8025, 8017,
+ 8026, 8026, 8027, 8019, 8028, 8028, 8029, 8021, 8030, 8030, 8031, 8023,
+ 8032, -8033, 8040, -8033, 8048, -8049, 8072, -8065, 8080, -8081, 8088, -8081,
+ 8096, -8097, 8104, -8097, 8112, -8113, 8120, -8113, 8122, -8049, 8124, 8115,
+ 8125, 32, 8126, 953, 8127, 32, 8130, -8131, 8136, -8051, 8140, 8131,
+ 8141, 32, 8144, -8145, 8152, -8145, 8154, -8055, 8156, 8156, 8157, 32,
+ 8160, -8161, 8168, -8161, 8170, -8059, 8172, 8165, 8173, 32, 8176, -8177,
+ 8184, -8057, 8186, -8061, 8188, 8179, 8189, 32, 8191, 8191, 8192, 32,
+ 8203, 0, 8208, 32, 8234, 0, 8239, 32, 8288, 0, 8293, 8293,
+ 8294, 0, 8304, -8305, 8314, 32, 8319, -8320, 8330, 32, 8335, -8336,
+ 8352, 32, 8385, -8386, 8400, 0, 8433, -8434, 8448, 32, 8450, 99,
+ 8452, 32, 8455, 603, 8456, 32, 8457, 102, 8458, 8458, 8459, 104,
+ 8462, -8463, 8464, 105, 8466, 108, 8467, 8467, 8468, 32, 8469, 110,
+ 8470, 32, 8473, -113, 8476, 114, 8478, 32, 8484, 122, 8485, 32,
+ 8486, 969, 8487, 32, 8488, 122, 8489, 32, 8490, 107, 8491, 229,
+ 8492, -99, 8494, 32, 8495, 8495, 8496, -102, 8498, 8526, 8499, 109,
+ 8500, -8501, 8506, 32, 8508, -8509, 8510, 947, 8511, 960, 8512, 32,
+ 8517, 100, 8518, -8519, 8522, 32, 8526, 8526, 8527, 32, 8528, -8529,
+ 8544, -8561, 8560, -8561, 8579, 8580, 8581, -8582, 8586, 32, 8588, -8589,
+ 8592, 32, 9255, -9256, 9280, 32, 9291, -9292, 9372, 32, 9398, -9425,
+ 9424, -9425, 9472, 32, 10102, -10103, 10132, 32, 11124, -11125, 11126, 32,
+ 11158, 11158, 11159, 32, 11264, -11313, 11312, -11313, 11360, 11361, 11362, 619,
+ 11363, 7549, 11364, 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371, 11372,
+ 11373, 593, 11374, 625, 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380,
+ 11381, 11382, 11383, -11384, 11389, 118, 11390, -576, 11392, 2097153, 11492, 11492,
+ 11493, 32, 11499, 11500, 11501, 11502, 11503, 0, 11506, 11507, 11508, -11509,
+ 11513, 32, 11517, 11517, 11518, 32, 11520, -11521, 11632, 32, 11633, -11634,
+ 11647, 0, 11648, -11649, 11744, 0, 11776, 32, 11823, 11823, 11824, 32,
+ 11870, -11871, 11904, 32, 11930, 11930, 11931, 32, 11935, 11935, 11936, 32,
+ 12019, -12020, 12272, 32, 12284, -12285, 12288, 32, 12293, -12294, 12296, 32,
+ 12321, -12322, 12330, 0, 12336, 32, 12337, -12338, 12342, 32, 12344, -12345,
+ 12349, 32, 12352, -12353, 12441, 0, 12443, 32, 12445, -12446, 12448, 32,
+ 12449, -12450, 12539, 32, 12540, 0, 12541, -12542, 12688, 32, 12690, -12691,
+ 12736, 32, 12772, -12773, 12800, 32, 12831, -12832, 12842, 32, 12868, -12869,
+ 12880, 32, 12881, -12882, 12910, 32, 12928, -12929, 12992, 32, 13008, -13009,
+ 13055, 32, 13312, -13313, 19904, 32, 19968, -19969, 42128, 32, 42183, -42184,
+ 42238, 32, 42240, -42241, 42509, 32, 42512, -42513, 42560, 2097153, 42606, 42606,
+ 42607, 0, 42611, 32, 42612, 0, 42622, 32, 42623, 2097153, 42652, -42653,
+ 42654, 0, 42656, -42657, 42736, 0, 42738, 32, 42744, -42745, 42752, 32,
+ 42775, -42776, 42784, 32, 42786, 2097153, 42800, -42801, 42802, 2097153, 42864, -42865,
+ 42873, 42874, 42875, 42876, 42877, 7545, 42878, 2097153, 42888, 42888, 42889, 32,
+ 42891, 42892, 42893, 613, 42894, -42895, 42896, 2097153, 42900, -42901, 42902, 2097153,
+ 42922, 614, 42923, 604, 42924, 609, 42925, 620, 42926, 618, 42927, 42927,
+ 42928, 670, 42929, 647, 42930, 669, 42931, 43859, 42932, 2097153, 42948, 42900,
+ 42949, 642, 42950, 7566, 42951, 42952, 42953, 42954, 42955, -42956, 42960, 42961,
+ 42962, -42963, 42966, 2097153, 42970, -42971, 42994, 99, 42995, 102, 42996, 113,
+ 42997, 42998, 42999, 42999, 43000, 295, 43001, -43002, 43010, 0, 43011, -43012,
+ 43014, 0, 43015, -43016, 43019, 0, 43020, -43021, 43043, 0, 43048, 32,
+ 43052, 0, 43053, -43054, 43062, 32, 43066, -43067, 43124, 32, 43128, -43129,
+ 43136, 0, 43138, -43139, 43188, 0, 43206, -43207, 43214, 32, 43216, -43217,
+ 43232, 0, 43250, -43251, 43256, 32, 43259, 43259, 43260, 32, 43261, -43262,
+ 43263, 0, 43264, -43265, 43302, 0, 43310, 32, 43312, -43313, 43335, 0,
+ 43348, -43349, 43359, 32, 43360, -43361, 43392, 0, 43396, -43397, 43443, 0,
+ 43457, 32, 43470, -43471, 43486, 32, 43488, -43489, 43493, 0, 43494, -43495,
+ 43561, 0, 43575, -43576, 43587, 0, 43588, -43589, 43596, 0, 43598, -43599,
+ 43612, 32, 43616, -43617, 43639, 32, 43642, 43642, 43643, 0, 43646, -43647,
+ 43696, 0, 43697, 43697, 43698, 0, 43701, -43702, 43703, 0, 43705, -43706,
+ 43710, 0, 43712, 43712, 43713, 0, 43714, -43715, 43742, 32, 43744, -43745,
+ 43755, 0, 43760, 32, 43762, -43763, 43765, 0, 43767, -43768, 43867, 32,
+ 43868, -43869, 43882, 32, 43884, -43885, 43888, -5025, 43968, -43969, 44003, 0,
+ 44011, 32, 44012, 0, 44014, -44015, 55296, 0, 57344, -57345, 64286, 0,
+ 64287, -64288, 64297, 32, 64298, -64299, 64434, 32, 64451, -64452, 64830, 32,
+ 64848, -64849, 64975, 32, 65008, -65009, 65020, 32, 65024, 0, 65040, 32,
+ 65050, -65051, 65056, 0, 65072, 32, 65107, 65107, 65108, 32, 65127, 65127,
+ 65128, 32, 65132, -65133, 65279, 0, 65280, 65280, 65281, 32, 65296, -65297,
+ 65306, 32, 65313, -65346, 65339, 32, 65345, -65346, 65371, 32, 65382, -65383,
+ 65504, 32, 65511, 65511, 65512, 32, 65519, -65520, 65529, 0, 65532, 32,
+ 65536, -65537, 65792, 32, 65795, -65796, 65847, 32, 65856, -65857, 65913, 32,
+ 65930, -65931, 65932, 32, 65935, 65935, 65936, 32, 65949, -65950, 65952, 32,
+ 65953, -65954, 66000, 32, 66045, 0, 66046, -66047, 66272, 0, 66273, -66274,
+ 66422, 0, 66427, -66428, 66463, 32, 66464, -66465, 66512, 32, 66513, -66514,
+ 66560, -66601, 66600, -66601, 66736, -66777, 66772, -66773, 66927, 32, 66928, -66968,
+ 66939, 66939, 66940, -66980, 66955, 66955, 66956, -66996, 66963, 66963, 66964, -67004,
+ 66966, -66967, 67671, 32, 67672, -67673, 67703, 32, 67705, -67706, 67871, 32,
+ 67872, -67873, 67903, 32, 67904, -67905, 68097, 0, 68100, 68100, 68101, 0,
+ 68103, -68104, 68108, 0, 68112, -68113, 68152, 0, 68155, -68156, 68159, 0,
+ 68160, -68161, 68176, 32, 68185, -68186, 68223, 32, 68224, -68225, 68296, 32,
+ 68297, -68298, 68325, 0, 68327, -68328, 68336, 32, 68343, -68344, 68409, 32,
+ 68416, -68417, 68505, 32, 68509, -68510, 68736, -68801, 68787, -68788, 68900, 0,
+ 68904, -68905, 69291, 0, 69293, 32, 69294, -69295, 69373, 0, 69376, -69377,
+ 69446, 0, 69457, -69458, 69461, 32, 69466, -69467, 69506, 0, 69510, 32,
+ 69514, -69515, 69632, 0, 69635, -69636, 69688, 0, 69703, 32, 69710, -69711,
+ 69744, 0, 69745, -69746, 69747, 0, 69749, -69750, 69759, 0, 69763, -69764,
+ 69808, 0, 69819, 32, 69821, 0, 69822, 32, 69826, 0, 69827, -69828,
+ 69837, 0, 69838, -69839, 69888, 0, 69891, -69892, 69927, 0, 69941, -69942,
+ 69952, 32, 69956, 69956, 69957, 0, 69959, -69960, 70003, 0, 70004, 32,
+ 70006, -70007, 70016, 0, 70019, -70020, 70067, 0, 70081, -70082, 70085, 32,
+ 70089, 0, 70093, 32, 70094, 0, 70096, -70097, 70107, 32, 70108, 70108,
+ 70109, 32, 70112, -70113, 70188, 0, 70200, 32, 70206, 0, 70207, -70208,
+ 70209, 0, 70210, -70211, 70313, 32, 70314, -70315, 70367, 0, 70379, -70380,
+ 70400, 0, 70404, -70405, 70459, 0, 70461, 70461, 70462, 0, 70469, -70470,
+ 70471, 0, 70473, -70474, 70475, 0, 70478, -70479, 70487, 0, 70488, -70489,
+ 70498, 0, 70500, -70501, 70502, 0, 70509, -70510, 70512, 0, 70517, -70518,
+ 70709, 0, 70727, -70728, 70731, 32, 70736, -70737, 70746, 32, 70748, 70748,
+ 70749, 32, 70750, 0, 70751, -70752, 70832, 0, 70852, -70853, 70854, 32,
+ 70855, -70856, 71087, 0, 71094, -71095, 71096, 0, 71105, 32, 71128, -71129,
+ 71132, 0, 71134, -71135, 71216, 0, 71233, 32, 71236, -71237, 71264, 32,
+ 71277, -71278, 71339, 0, 71352, 71352, 71353, 32, 71354, -71355, 71453, 0,
+ 71468, -71469, 71484, 32, 71488, -71489, 71724, 0, 71739, 32, 71740, -71741,
+ 71840, -71873, 71872, -71873, 71984, 0, 71990, 71990, 71991, 0, 71993, -71994,
+ 71995, 0, 71999, 71999, 72000, 0, 72001, 72001, 72002, 0, 72004, 32,
+ 72007, -72008, 72145, 0, 72152, -72153, 72154, 0, 72161, 72161, 72162, 32,
+ 72163, 72163, 72164, 0, 72165, -72166, 72193, 0, 72203, -72204, 72243, 0,
+ 72250, 72250, 72251, 0, 72255, 32, 72263, 0, 72264, -72265, 72273, 0,
+ 72284, -72285, 72330, 0, 72346, 32, 72349, 72349, 72350, 32, 72355, -72356,
+ 72448, 32, 72458, -72459, 72751, 0, 72759, 72759, 72760, 0, 72768, 72768,
+ 72769, 32, 72774, -72775, 72816, 32, 72818, -72819, 72850, 0, 72872, 72872,
+ 72873, 0, 72887, -72888, 73009, 0, 73015, -73016, 73018, 0, 73019, 73019,
+ 73020, 0, 73022, 73022, 73023, 0, 73030, 73030, 73031, 0, 73032, -73033,
+ 73098, 0, 73103, 73103, 73104, 0, 73106, 73106, 73107, 0, 73112, -73113,
+ 73459, 0, 73463, 32, 73465, -73466, 73472, 0, 73474, 73474, 73475, 0,
+ 73476, -73477, 73524, 0, 73531, -73532, 73534, 0, 73539, 32, 73552, -73553,
+ 73685, 32, 73714, -73715, 73727, 32, 73728, -73729, 74864, 32, 74869, -74870,
+ 77809, 32, 77811, -77812, 78896, 0, 78913, -78914, 78919, 0, 78934, -78935,
+ 92782, 32, 92784, -92785, 92912, 0, 92917, 32, 92918, -92919, 92976, 0,
+ 92983, 32, 92992, -92993, 92996, 32, 92998, -92999, 93760, -93793, 93792, -93793,
+ 93847, 32, 93851, -93852, 94031, 0, 94032, 94032, 94033, 0, 94088, -94089,
+ 94095, 0, 94099, -94100, 94178, 32, 94179, 94179, 94180, 0, 94181, -94182,
+ 94192, 0, 94194, -94195, 113820, 32, 113821, 0, 113823, 32, 113824, 0,
+ 113828, -113829, 118528, 0, 118574, -118575, 118576, 0, 118599, -118600, 118608, 32,
+ 118724, -118725, 118784, 32, 119030, -119031, 119040, 32, 119079, -119080, 119081, 32,
+ 119141, 0, 119146, 32, 119149, 0, 119171, 32, 119173, 0, 119180, 32,
+ 119210, 0, 119214, 32, 119275, -119276, 119296, 32, 119362, 0, 119365, 32,
+ 119366, -119367, 119552, 32, 119639, -119640, 119808, -98, 119834, -119835, 119860, -98,
+ 119886, -119887, 119912, -98, 119938, -119939, 119964, 97, 119965, 119965, 119966, -100,
+ 119968, -119969, 119970, 103, 119971, -119972, 119973, -107, 119975, -119976, 119977, -111,
+ 119981, 119981, 119982, -116, 119990, -119991, 120016, -98, 120042, -120043, 120068, -98,
+ 120070, 120070, 120071, -101, 120075, -120076, 120077, -107, 120085, 120085, 120086, -116,
+ 120093, -120094, 120120, -98, 120122, 120122, 120123, -101, 120127, 120127, 120128, -106,
+ 120133, 120133, 120134, 111, 120135, -120136, 120138, -116, 120145, -120146, 120172, -98,
+ 120198, -120199, 120224, -98, 120250, -120251, 120276, -98, 120302, -120303, 120328, -98,
+ 120354, -120355, 120380, -98, 120406, -120407, 120432, -98, 120458, -120459, 120488, -946,
+ 120505, 952, 120506, -964, 120513, 32, 120514, -120515, 120531, 963, 120532, -120533,
+ 120539, 32, 120540, -120541, 120546, -946, 120563, 952, 120564, -964, 120571, 32,
+ 120572, -120573, 120589, 963, 120590, -120591, 120597, 32, 120598, -120599, 120604, -946,
+ 120621, 952, 120622, -964, 120629, 32, 120630, -120631, 120647, 963, 120648, -120649,
+ 120655, 32, 120656, -120657, 120662, -946, 120679, 952, 120680, -964, 120687, 32,
+ 120688, -120689, 120705, 963, 120706, -120707, 120713, 32, 120714, -120715, 120720, -946,
+ 120737, 952, 120738, -964, 120745, 32, 120746, -120747, 120763, 963, 120764, -120765,
+ 120771, 32, 120772, -120773, 120778, 989, 120779, -120780, 120832, 32, 121344, 0,
+ 121399, 32, 121403, 0, 121453, 32, 121461, 0, 121462, 32, 121476, 0,
+ 121477, 32, 121484, -121485, 121499, 0, 121504, 121504, 121505, 0, 121520, -121521,
+ 122880, 0, 122887, 122887, 122888, 0, 122905, -122906, 122907, 0, 122914, 122914,
+ 122915, 0, 122917, 122917, 122918, 0, 122923, -122924, 123023, 0, 123024, -123025,
+ 123184, 0, 123191, -123192, 123215, 32, 123216, -123217, 123566, 0, 123567, -123568,
+ 123628, 0, 123632, -123633, 123647, 32, 123648, -123649, 124140, 0, 124144, -124145,
+ 125136, 0, 125143, -125144, 125184, -125219, 125218, -125219, 125252, 0, 125259, -125260,
+ 125278, 32, 125280, -125281, 126124, 32, 126125, -126126, 126128, 32, 126129, -126130,
+ 126254, 32, 126255, -126256, 126704, 32, 126706, -126707, 126976, 32, 127020, -127021,
+ 127024, 32, 127124, -127125, 127136, 32, 127151, -127152, 127153, 32, 127168, 127168,
+ 127169, 32, 127184, 127184, 127185, 32, 127222, -127223, 127245, 32, 127275, 99,
+ 127276, 114, 127277, 32, 127280, -98, 127306, 32, 127406, -127407, 127462, 32,
+ 127490, -127491, 127552, 32, 127561, -127562, 127584, 32, 127590, -127591, 127744, 32,
+ 128728, -128729, 128732, 32, 128749, -128750, 128752, 32, 128765, -128766, 128768, 32,
+ 128887, -128888, 128891, 32, 128986, -128987, 128992, 32, 129004, -129005, 129008, 32,
+ 129009, -129010, 129024, 32, 129036, -129037, 129040, 32, 129096, -129097, 129104, 32,
+ 129114, -129115, 129120, 32, 129160, -129161, 129168, 32, 129198, -129199, 129200, 32,
+ 129202, -129203, 129280, 32, 129620, -129621, 129632, 32, 129646, -129647, 129648, 32,
+ 129661, -129662, 129664, 32, 129673, -129674, 129680, 32, 129726, 129726, 129727, 32,
+ 129734, -129735, 129742, 32, 129756, -129757, 129760, 32, 129769, -129770, 129776, 32,
+ 129785, -129786, 129792, 32, 129939, 129939, 129940, 32, 129995, -129996, 131070, 32,
+ 131072, -131073, 196606, 32, 196608, -196609, 262142, 32, 262144, -262145, 327678, 32,
+ 327680, -327681, 393214, 32, 393216, -393217, 458750, 32, 458752, -458753, 524286, 32,
+ 524288, -524289, 589822, 32, 589824, -589825, 655358, 32, 655360, -655361, 720894, 32,
+ 720896, -720897, 786430, 32, 786432, -786433, 851966, 32, 851968, -851969, 917502, 32,
+ 917504, 917504, 917505, 0, 917506, -917507, 917536, 0, 917632, -917633, 917760, 0,
+ 918000, -918001, 983038, 32, 983040, -983041, 1048574, 32, 1048576, -1048577, 1114110, 32,
+ 2147483647, 0};
-static int16 to_lower_table[TABLE_SIZE] = {
+static const int16 to_lower_table[TABLE_SIZE] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
@@ -485,33 +597,680 @@ static int16 to_lower_table[TABLE_SIZE] = {
1273, 1275, 1275, 1277, 1277, 1279, 1279};
static const int32 to_lower_ranges[] = {
- 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 4256, -11521, 4294, 4294, 4295,
- 11559, 4296, -4297, 4301, 11565, 4302, -4303, 5024, -43889, 5104, -5113, 5110, -5111,
- 7680, 2097153, 7830, -7831, 7838, 223, 7839, 2097153, 7936, -7937, 7944, -7937, 7952,
- -7953, 7960, -7953, 7966, -7967, 7976, -7969, 7984, -7985, 7992, -7985, 8000, -8001,
- 8008, -8001, 8014, -8015, 8025, 8017, 8026, 8026, 8027, 8019, 8028, 8028, 8029,
- 8021, 8030, 8030, 8031, 8023, 8032, -8033, 8040, -8033, 8048, -8049, 8072, -8065,
- 8080, -8081, 8088, -8081, 8096, -8097, 8104, -8097, 8112, -8113, 8120, -8113, 8122,
- -8049, 8124, 8115, 8125, -8126, 8136, -8051, 8140, 8131, 8141, -8142, 8152, -8145,
- 8154, -8055, 8156, -8157, 8168, -8161, 8170, -8059, 8172, 8165, 8173, -8174, 8184,
- -8057, 8186, -8061, 8188, 8179, 8189, -8190, 8486, 969, 8487, -8488, 8490, 107,
- 8491, 229, 8492, -8493, 8498, 8526, 8499, -8500, 8544, -8561, 8560, -8561, 8579,
- 8580, 8581, -8582, 9398, -9425, 9424, -9425, 11264, -11313, 11311, -11312, 11360, 11361,
- 11362, 619, 11363, 7549, 11364, 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371,
- 11372, 11373, 593, 11374, 625, 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380,
- 11381, 11382, 11383, -11384, 11390, -576, 11392, 2097153, 11492, -11493, 11499, 11500, 11501,
- 11502, 11503, -11504, 11506, 11507, 11508, -11509, 42560, 2097153, 42606, -42607, 42624, 2097153,
- 42652, -42653, 42786, 2097153, 42800, -42801, 42802, 2097153, 42864, -42865, 42873, 42874, 42875,
- 42876, 42877, 7545, 42878, 2097153, 42888, -42889, 42891, 42892, 42893, 613, 42894, -42895,
- 42896, 2097153, 42900, -42901, 42902, 2097153, 42922, 614, 42923, 604, 42924, 609, 42925,
- 620, 42926, 618, 42927, 42927, 42928, 670, 42929, 647, 42930, 669, 42931, 43859,
- 42932, 2097153, 42936, -42937, 65313, -65346, 65339, -65340, 66560, -66601, 66600, -66601, 66736,
- -66777, 66772, -66773, 68736, -68801, 68787, -68788, 71840, -71873, 71872, -71873, 125184, -125219,
- 125218, -125219, 2147483647, 0};
+ 1280, 2097153, 1328, 1328, 1329, -1378, 1367, -1368, 4256, -11521, 4294, 4294, 4295,
+ 11559, 4296, -4297, 4301, 11565, 4302, -4303, 5024, -43889, 5104, -5113, 5110, -5111,
+ 7312, -4305, 7355, -7356, 7357, -4350, 7360, -7361, 7680, 2097153, 7830, -7831, 7838,
+ 223, 7839, 2097153, 7936, -7937, 7944, -7937, 7952, -7953, 7960, -7953, 7966, -7967,
+ 7976, -7969, 7984, -7985, 7992, -7985, 8000, -8001, 8008, -8001, 8014, -8015, 8025,
+ 8017, 8026, 8026, 8027, 8019, 8028, 8028, 8029, 8021, 8030, 8030, 8031, 8023,
+ 8032, -8033, 8040, -8033, 8048, -8049, 8072, -8065, 8080, -8081, 8088, -8081, 8096,
+ -8097, 8104, -8097, 8112, -8113, 8120, -8113, 8122, -8049, 8124, 8115, 8125, -8126,
+ 8136, -8051, 8140, 8131, 8141, -8142, 8152, -8145, 8154, -8055, 8156, -8157, 8168,
+ -8161, 8170, -8059, 8172, 8165, 8173, -8174, 8184, -8057, 8186, -8061, 8188, 8179,
+ 8189, -8190, 8486, 969, 8487, -8488, 8490, 107, 8491, 229, 8492, -8493, 8498,
+ 8526, 8499, -8500, 8544, -8561, 8560, -8561, 8579, 8580, 8581, -8582, 9398, -9425,
+ 9424, -9425, 11264, -11313, 11312, -11313, 11360, 11361, 11362, 619, 11363, 7549, 11364,
+ 637, 11365, -11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 593, 11374, 625,
+ 11375, 592, 11376, 594, 11377, 2097153, 11380, 11380, 11381, 11382, 11383, -11384, 11390,
+ -576, 11392, 2097153, 11492, -11493, 11499, 11500, 11501, 11502, 11503, -11504, 11506, 11507,
+ 11508, -11509, 42560, 2097153, 42606, -42607, 42624, 2097153, 42652, -42653, 42786, 2097153, 42800,
+ -42801, 42802, 2097153, 42864, -42865, 42873, 42874, 42875, 42876, 42877, 7545, 42878, 2097153,
+ 42888, -42889, 42891, 42892, 42893, 613, 42894, -42895, 42896, 2097153, 42900, -42901, 42902,
+ 2097153, 42922, 614, 42923, 604, 42924, 609, 42925, 620, 42926, 618, 42927, 42927,
+ 42928, 670, 42929, 647, 42930, 669, 42931, 43859, 42932, 2097153, 42948, 42900, 42949,
+ 642, 42950, 7566, 42951, 42952, 42953, 42954, 42955, -42956, 42960, 42961, 42962, -42963,
+ 42966, 2097153, 42970, -42971, 42997, 42998, 42999, -43000, 65313, -65346, 65339, -65340, 66560,
+ -66601, 66600, -66601, 66736, -66777, 66772, -66773, 66928, -66968, 66939, 66939, 66940, -66980,
+ 66955, 66955, 66956, -66996, 66963, 66963, 66964, -67004, 66966, -66967, 68736, -68801, 68787,
+ -68788, 71840, -71873, 71872, -71873, 93760, -93793, 93792, -93793, 125184, -125219, 125218, -125219,
+ 2147483647, 0};
+
+static const int16 without_diacritics_table[TABLE_SIZE] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 32, 161, 162, 163, 164, 165, 166, 167, 32, 169, 97,
+ 171, 172, 0, 174, 32, 176, 177, 50, 51, 32, 956, 182, 0, 32, 49, 111, 187, 188, 189,
+ 190, 191, 65, 65, 65, 65, 65, 65, 198, 67, 69, 69, 69, 69, 73, 73, 73, 73, 208,
+ 78, 79, 79, 79, 79, 79, 215, 216, 85, 85, 85, 85, 89, 222, 223, 97, 97, 97, 97,
+ 97, 97, 230, 99, 101, 101, 101, 101, 105, 105, 105, 105, 240, 110, 111, 111, 111, 111, 111,
+ 247, 248, 117, 117, 117, 117, 121, 254, 121, 65, 97, 65, 97, 65, 97, 67, 99, 67, 99,
+ 67, 99, 67, 99, 68, 100, 272, 273, 69, 101, 69, 101, 69, 101, 69, 101, 69, 101, 71,
+ 103, 71, 103, 71, 103, 71, 103, 72, 104, 294, 295, 73, 105, 73, 105, 73, 105, 73, 105,
+ 73, 305, 306, 307, 74, 106, 75, 107, 312, 76, 108, 76, 108, 76, 108, 76, 108, 321, 322,
+ 78, 110, 78, 110, 78, 110, 110, 330, 331, 79, 111, 79, 111, 79, 111, 338, 339, 82, 114,
+ 82, 114, 82, 114, 83, 115, 83, 115, 83, 115, 83, 115, 84, 116, 84, 116, 358, 359, 85,
+ 117, 85, 117, 85, 117, 85, 117, 85, 117, 85, 117, 87, 119, 89, 121, 89, 90, 122, 90,
+ 122, 90, 122, 115, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398,
+ 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 79, 111,
+ 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 85, 117, 433, 434, 435, 436,
+ 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455,
+ 456, 457, 458, 459, 460, 65, 97, 73, 105, 79, 111, 85, 117, 85, 117, 85, 117, 85, 117,
+ 85, 117, 477, 65, 97, 65, 97, 198, 230, 484, 485, 71, 103, 75, 107, 79, 111, 79, 111,
+ 439, 658, 106, 497, 498, 499, 71, 103, 502, 503, 78, 110, 65, 97, 198, 230, 216, 248, 65,
+ 97, 65, 97, 69, 101, 69, 101, 73, 105, 73, 105, 79, 111, 79, 111, 82, 114, 82, 114,
+ 85, 117, 85, 117, 83, 115, 84, 116, 540, 541, 72, 104, 544, 545, 546, 547, 548, 549, 65,
+ 97, 69, 101, 79, 111, 79, 111, 79, 111, 79, 111, 89, 121, 564, 565, 566, 567, 568, 569,
+ 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588,
+ 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607,
+ 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626,
+ 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645,
+ 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664,
+ 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683,
+ 684, 685, 686, 687, 104, 614, 106, 114, 633, 635, 641, 119, 121, 697, 698, 699, 0, 701, 0,
+ 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721,
+ 722, 723, 724, 725, 726, 727, 32, 32, 32, 32, 32, 32, 734, 735, 611, 108, 115, 120, 661,
+ 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759,
+ 760, 761, 762, 763, 764, 765, 766, 767, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 837, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 880, 881, 882, 883, 697, 885, 886, 887, 888, 889, 32, 891, 892,
+ 893, 59, 895, 896, 897, 898, 899, 32, 32, 913, 903, 917, 919, 921, 907, 927, 909, 933, 937,
+ 953, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930,
+ 931, 932, 933, 934, 935, 936, 937, 921, 933, 945, 949, 951, 953, 965, 945, 946, 947, 948, 949,
+ 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968,
+ 969, 953, 965, 959, 965, 969, 975, 946, 952, 933, 933, 933, 966, 960, 983, 984, 985, 986, 987,
+ 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006,
+ 1007, 954, 961, 962, 1011, 920, 949, 1014, 1015, 1016, 931, 1018, 1019, 1020, 1021, 1022, 1023, 1045, 1045,
+ 1026, 1043, 1028, 1029, 1030, 1030, 1032, 1033, 1034, 1035, 1050, 1048, 1059, 1039, 1040, 1041, 1042, 1043, 1044,
+ 1045, 1046, 1047, 1048, 1048, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063,
+ 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1080, 1082,
+ 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1101,
+ 1102, 1103, 1077, 1077, 1106, 1075, 1108, 1109, 1110, 1110, 1112, 1113, 1114, 1115, 1082, 1080, 1091, 1119, 1120,
+ 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139,
+ 1140, 1141, 1140, 1141, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 0, 0, 0, 0,
+ 0, 0, 0, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177,
+ 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196,
+ 1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215,
+ 1216, 1046, 1078, 1219, 1220, 1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1040, 1072, 1040,
+ 1072, 1236, 1237, 1045, 1077, 1240, 1241, 1240, 1241, 1046, 1078, 1047, 1079, 1248, 1249, 1048, 1080, 1048, 1080,
+ 1054, 1086, 1256, 1257, 1256, 1257, 1069, 1101, 1059, 1091, 1059, 1091, 1059, 1091, 1063, 1095, 1270, 1271, 1067,
+ 1099, 1274, 1275, 1276, 1277, 1278, 1279};
+
+static const int32 without_diacritics_ranges[] = {
+ 1280, -1281, 1425, 0, 1470, 1470, 1471, 0, 1472, 1472, 1473, 0,
+ 1475, 1475, 1476, 0, 1478, 1478, 1479, 0, 1480, -1481, 1536, 0,
+ 1542, -1543, 1552, 0, 1563, 1563, 1564, 0, 1565, -1566, 1570, 1575,
+ 1572, 1608, 1573, 1575, 1574, 1610, 1575, -1576, 1611, 0, 1632, -1633,
+ 1648, 0, 1649, -1650, 1728, 1749, 1729, 2097154, 1732, -1733, 1747, 2097152,
+ 1749, 1749, 1750, 0, 1758, 1758, 1759, 0, 1765, -1766, 1767, 0,
+ 1769, 1769, 1770, 0, 1774, -1775, 1807, 0, 1808, 1808, 1809, 0,
+ 1810, -1811, 1840, 0, 1867, -1868, 1958, 0, 1969, -1970, 2027, 0,
+ 2036, -2037, 2045, 0, 2046, -2047, 2070, 0, 2074, 2074, 2075, 0,
+ 2084, 2084, 2085, 0, 2088, 2088, 2089, 0, 2094, -2095, 2137, 0,
+ 2140, -2141, 2192, 0, 2194, -2195, 2200, 0, 2208, -2209, 2250, 0,
+ 2308, -2309, 2345, 2097152, 2347, -2348, 2353, 2097152, 2355, 2097154, 2358, -2359,
+ 2362, 0, 2365, 2365, 2366, 0, 2384, 2384, 2385, 0, 2392, -2326,
+ 2395, 2332, 2396, -2338, 2398, 2347, 2399, 2351, 2400, -2401, 2402, 0,
+ 2404, -2405, 2433, 0, 2436, -2437, 2492, 0, 2493, 2493, 2494, 0,
+ 2501, -2502, 2503, 0, 2505, -2506, 2507, 0, 2510, -2511, 2519, 0,
+ 2520, -2521, 2524, -2466, 2526, 2526, 2527, 2479, 2528, -2529, 2530, 0,
+ 2532, -2533, 2558, 0, 2559, -2560, 2561, 0, 2564, -2565, 2611, 2097152,
+ 2613, 2613, 2614, 2616, 2615, -2616, 2620, 0, 2621, 2621, 2622, 0,
+ 2627, -2628, 2631, 0, 2633, -2634, 2635, 0, 2638, -2639, 2641, 0,
+ 2642, -2643, 2649, -2583, 2651, 2588, 2652, -2653, 2654, 2603, 2655, -2656,
+ 2672, 0, 2674, -2675, 2677, 0, 2678, -2679, 2689, 0, 2692, -2693,
+ 2748, 0, 2749, 2749, 2750, 0, 2758, 2758, 2759, 0, 2762, 2762,
+ 2763, 0, 2766, -2767, 2786, 0, 2788, -2789, 2810, 0, 2816, 2816,
+ 2817, 0, 2820, -2821, 2876, 0, 2877, 2877, 2878, 0, 2885, -2886,
+ 2887, 0, 2889, -2890, 2891, 0, 2894, -2895, 2901, 0, 2904, -2905,
+ 2908, -2850, 2910, -2911, 2914, 0, 2916, -2917, 2946, 0, 2947, -2948,
+ 2964, 2962, 2965, -2966, 3006, 0, 3011, -3012, 3014, 0, 3017, 3017,
+ 3018, 0, 3022, -3023, 3031, 0, 3032, -3033, 3072, 0, 3077, -3078,
+ 3132, 0, 3133, 3133, 3134, 0, 3141, 3141, 3142, 0, 3145, 3145,
+ 3146, 0, 3150, -3151, 3157, 0, 3159, -3160, 3170, 0, 3172, -3173,
+ 3201, 0, 3204, -3205, 3260, 0, 3261, 3261, 3262, 0, 3269, 3269,
+ 3270, 0, 3273, 3273, 3274, 0, 3278, -3279, 3285, 0, 3287, -3288,
+ 3298, 0, 3300, -3301, 3315, 0, 3316, -3317, 3328, 0, 3332, -3333,
+ 3387, 0, 3389, 3389, 3390, 0, 3397, 3397, 3398, 0, 3401, 3401,
+ 3402, 0, 3406, -3407, 3415, 0, 3416, -3417, 3426, 0, 3428, -3429,
+ 3457, 0, 3460, -3461, 3530, 0, 3531, -3532, 3535, 0, 3541, 3541,
+ 3542, 0, 3543, 3543, 3544, 0, 3552, -3553, 3570, 0, 3572, -3573,
+ 3633, 0, 3634, 3634, 3636, 0, 3643, -3644, 3655, 0, 3663, -3664,
+ 3761, 0, 3762, 3762, 3764, 0, 3773, -3774, 3784, 0, 3791, -3792,
+ 3852, 2097154, 3854, -3855, 3864, 0, 3866, -3867, 3893, 0, 3894, 3894,
+ 3895, 0, 3896, 3896, 3897, 0, 3898, -3899, 3902, 0, 3904, -3905,
+ 3907, 2097152, 3909, -3910, 3917, 2097152, 3919, -3920, 3922, 2097154, 3924, -3925,
+ 3927, 2097152, 3929, -3930, 3932, 2097154, 3934, -3935, 3945, 3904, 3946, -3947,
+ 3953, 0, 3973, 3973, 3974, 0, 3976, -3977, 3981, 0, 3992, 3992,
+ 3993, 0, 4029, -4030, 4038, 0, 4039, -4040, 4134, 2097154, 4136, -4137,
+ 4139, 0, 4159, -4160, 4182, 0, 4186, -4187, 4190, 0, 4193, 4193,
+ 4194, 0, 4197, -4198, 4199, 0, 4206, -4207, 4209, 0, 4213, -4214,
+ 4226, 0, 4238, 4238, 4239, 0, 4240, -4241, 4250, 0, 4254, -4255,
+ 4348, 4316, 4349, -4350, 4957, 0, 4960, -4961, 5906, 0, 5910, -5911,
+ 5938, 0, 5941, -5942, 5970, 0, 5972, -5973, 6002, 0, 6004, -6005,
+ 6068, 0, 6100, -6101, 6109, 0, 6110, -6111, 6155, 0, 6160, -6161,
+ 6277, 0, 6279, -6280, 6313, 0, 6314, -6315, 6432, 0, 6444, -6445,
+ 6448, 0, 6460, -6461, 6679, 0, 6684, -6685, 6741, 0, 6751, 6751,
+ 6752, 0, 6781, -6782, 6783, 0, 6784, -6785, 6832, 0, 6863, -6864,
+ 6912, 0, 6917, 2097154, 6928, -6929, 6930, 2097154, 6932, -6933, 6964, 0,
+ 6981, -6982, 7019, 0, 7028, -7029, 7040, 0, 7043, -7044, 7073, 0,
+ 7086, -7087, 7142, 0, 7156, -7157, 7204, 0, 7224, -7225, 7376, 0,
+ 7379, 7379, 7380, 0, 7401, -7402, 7405, 0, 7406, -7407, 7412, 0,
+ 7413, -7414, 7415, 0, 7418, -7419, 7468, 65, 7469, 198, 7470, 66,
+ 7471, 7471, 7472, -69, 7474, 398, 7475, -72, 7483, 7483, 7484, 79,
+ 7485, 546, 7486, 80, 7487, 82, 7488, -85, 7490, 87, 7491, 97,
+ 7492, -593, 7494, 7426, 7495, 98, 7496, -101, 7498, 601, 7499, -604,
+ 7501, 103, 7502, 7502, 7503, 107, 7504, 109, 7505, 331, 7506, 111,
+ 7507, 596, 7508, -7447, 7510, 112, 7511, -117, 7513, 7453, 7514, 623,
+ 7515, 118, 7516, 7461, 7517, -947, 7520, -967, 7522, 105, 7523, 114,
+ 7524, -118, 7526, -947, 7528, 961, 7529, -967, 7531, -7532, 7544, 1085,
+ 7545, -7546, 7579, 594, 7580, 99, 7581, 597, 7582, 240, 7583, 604,
+ 7584, 102, 7585, 607, 7586, 609, 7587, 613, 7588, -617, 7591, 7547,
+ 7592, 669, 7593, 621, 7594, 7557, 7595, 671, 7596, 625, 7597, 624,
+ 7598, -627, 7602, 632, 7603, -643, 7605, 427, 7606, -650, 7608, 7452,
+ 7609, -652, 7611, 122, 7612, -657, 7615, 952, 7616, 0, 7680, 65,
+ 7681, 97, 7682, 66, 7683, 98, 7684, 66, 7685, 98, 7686, 66,
+ 7687, 98, 7688, 67, 7689, 99, 7690, 68, 7691, 100, 7692, 68,
+ 7693, 100, 7694, 68, 7695, 100, 7696, 68, 7697, 100, 7698, 68,
+ 7699, 100, 7700, 69, 7701, 101, 7702, 69, 7703, 101, 7704, 69,
+ 7705, 101, 7706, 69, 7707, 101, 7708, 69, 7709, 101, 7710, 70,
+ 7711, 102, 7712, 71, 7713, 103, 7714, 72, 7715, 104, 7716, 72,
+ 7717, 104, 7718, 72, 7719, 104, 7720, 72, 7721, 104, 7722, 72,
+ 7723, 104, 7724, 73, 7725, 105, 7726, 73, 7727, 105, 7728, 75,
+ 7729, 107, 7730, 75, 7731, 107, 7732, 75, 7733, 107, 7734, 76,
+ 7735, 108, 7736, 76, 7737, 108, 7738, 76, 7739, 108, 7740, 76,
+ 7741, 108, 7742, 77, 7743, 109, 7744, 77, 7745, 109, 7746, 77,
+ 7747, 109, 7748, 78, 7749, 110, 7750, 78, 7751, 110, 7752, 78,
+ 7753, 110, 7754, 78, 7755, 110, 7756, 79, 7757, 111, 7758, 79,
+ 7759, 111, 7760, 79, 7761, 111, 7762, 79, 7763, 111, 7764, 80,
+ 7765, 112, 7766, 80, 7767, 112, 7768, 82, 7769, 114, 7770, 82,
+ 7771, 114, 7772, 82, 7773, 114, 7774, 82, 7775, 114, 7776, 83,
+ 7777, 115, 7778, 83, 7779, 115, 7780, 83, 7781, 115, 7782, 83,
+ 7783, 115, 7784, 83, 7785, 115, 7786, 84, 7787, 116, 7788, 84,
+ 7789, 116, 7790, 84, 7791, 116, 7792, 84, 7793, 116, 7794, 85,
+ 7795, 117, 7796, 85, 7797, 117, 7798, 85, 7799, 117, 7800, 85,
+ 7801, 117, 7802, 85, 7803, 117, 7804, 86, 7805, 118, 7806, 86,
+ 7807, 118, 7808, 87, 7809, 119, 7810, 87, 7811, 119, 7812, 87,
+ 7813, 119, 7814, 87, 7815, 119, 7816, 87, 7817, 119, 7818, 88,
+ 7819, 120, 7820, 88, 7821, 120, 7822, 89, 7823, 121, 7824, 90,
+ 7825, 122, 7826, 90, 7827, 122, 7828, 90, 7829, 122, 7830, 104,
+ 7831, 116, 7832, 119, 7833, 121, 7834, 97, 7835, 115, 7836, -7837,
+ 7840, 65, 7841, 97, 7842, 65, 7843, 97, 7844, 65, 7845, 97,
+ 7846, 65, 7847, 97, 7848, 65, 7849, 97, 7850, 65, 7851, 97,
+ 7852, 65, 7853, 97, 7854, 65, 7855, 97, 7856, 65, 7857, 97,
+ 7858, 65, 7859, 97, 7860, 65, 7861, 97, 7862, 65, 7863, 97,
+ 7864, 69, 7865, 101, 7866, 69, 7867, 101, 7868, 69, 7869, 101,
+ 7870, 69, 7871, 101, 7872, 69, 7873, 101, 7874, 69, 7875, 101,
+ 7876, 69, 7877, 101, 7878, 69, 7879, 101, 7880, 73, 7881, 105,
+ 7882, 73, 7883, 105, 7884, 79, 7885, 111, 7886, 79, 7887, 111,
+ 7888, 79, 7889, 111, 7890, 79, 7891, 111, 7892, 79, 7893, 111,
+ 7894, 79, 7895, 111, 7896, 79, 7897, 111, 7898, 79, 7899, 111,
+ 7900, 79, 7901, 111, 7902, 79, 7903, 111, 7904, 79, 7905, 111,
+ 7906, 79, 7907, 111, 7908, 85, 7909, 117, 7910, 85, 7911, 117,
+ 7912, 85, 7913, 117, 7914, 85, 7915, 117, 7916, 85, 7917, 117,
+ 7918, 85, 7919, 117, 7920, 85, 7921, 117, 7922, 89, 7923, 121,
+ 7924, 89, 7925, 121, 7926, 89, 7927, 121, 7928, 89, 7929, 121,
+ 7930, -7931, 7936, 945, 7944, 913, 7952, 949, 7958, -7959, 7960, 917,
+ 7966, -7967, 7968, 951, 7976, 919, 7984, 953, 7992, 921, 8000, 959,
+ 8006, -8007, 8008, 927, 8014, -8015, 8016, 965, 8024, 8024, 8025, 933,
+ 8026, 8026, 8027, 933, 8028, 8028, 8029, 933, 8030, 8030, 8031, 933,
+ 8032, 969, 8040, 937, 8048, 945, 8050, 949, 8052, 951, 8054, 953,
+ 8056, 959, 8058, 965, 8060, 969, 8062, -8063, 8064, 945, 8072, 913,
+ 8080, 951, 8088, 919, 8096, 969, 8104, 937, 8112, 945, 8117, 8117,
+ 8118, 945, 8120, 913, 8125, 32, 8126, 953, 8127, 32, 8130, 951,
+ 8133, 8133, 8134, 951, 8136, 917, 8138, 919, 8141, 32, 8144, 953,
+ 8148, -8149, 8150, 953, 8152, 921, 8156, 8156, 8157, 32, 8160, 965,
+ 8164, 961, 8166, 965, 8168, 933, 8172, 929, 8173, 32, 8175, 96,
+ 8176, -8177, 8178, 969, 8181, 8181, 8182, 969, 8184, 927, 8186, 937,
+ 8189, 32, 8191, 8191, 8192, 32, 8203, 0, 8208, 2097152, 8211, -8212,
+ 8215, 32, 8216, -8217, 8228, 46, 8229, -8230, 8234, 0, 8239, 32,
+ 8240, -8241, 8254, 32, 8255, -8256, 8287, 32, 8288, 0, 8293, 8293,
+ 8294, 0, 8304, 48, 8305, 105, 8306, -8307, 8308, -53, 8314, 43,
+ 8315, 8722, 8316, 61, 8317, -41, 8319, 110, 8320, -49, 8330, 43,
+ 8331, 8722, 8332, 61, 8333, -41, 8335, 8335, 8336, 97, 8337, 101,
+ 8338, 111, 8339, 120, 8340, 601, 8341, 104, 8342, -108, 8346, 112,
+ 8347, -116, 8349, -8350, 8400, 0, 8433, -8434, 8450, 67, 8452, -8453,
+ 8455, 400, 8456, 8456, 8457, 70, 8458, 103, 8459, 72, 8462, 104,
+ 8463, 295, 8464, 73, 8466, 76, 8467, 108, 8468, 8468, 8469, 78,
+ 8470, -8471, 8473, -81, 8476, 82, 8478, -8479, 8484, 90, 8485, 8485,
+ 8486, 937, 8487, 8487, 8488, 90, 8489, 8489, 8490, 75, 8491, -66,
+ 8494, 8494, 8495, 101, 8496, -70, 8498, 8498, 8499, 77, 8500, 111,
+ 8501, -1489, 8505, 105, 8506, -8507, 8508, 960, 8509, 947, 8510, 915,
+ 8511, 928, 8512, 8721, 8513, -8514, 8517, 68, 8518, -101, 8520, -106,
+ 8522, -8523, 8543, 49, 8544, 73, 8545, -8546, 8548, 86, 8549, -8550,
+ 8553, 88, 8554, -8555, 8556, 76, 8557, -68, 8559, 77, 8560, 105,
+ 8561, -8562, 8564, 118, 8565, -8566, 8569, 120, 8570, -8571, 8572, 108,
+ 8573, -100, 8575, 109, 8576, -8577, 8602, 8592, 8603, 8594, 8604, -8605,
+ 8622, 8596, 8623, -8624, 8653, 8656, 8654, 8660, 8655, 8658, 8656, -8657,
+ 8708, 2097154, 8710, -8711, 8713, 2097152, 8715, 2097154, 8718, -8719, 8740, 2097154,
+ 8744, -8745, 8769, 8764, 8770, -8771, 8772, 2097154, 8774, 8774, 8775, 8773,
+ 8776, 2097152, 8779, -8780, 8800, 61, 8801, 2097154, 8804, -8805, 8813, 8781,
+ 8814, 60, 8815, 62, 8816, -8805, 8818, -8819, 8820, -8819, 8822, -8823,
+ 8824, -8823, 8826, -8827, 8832, -8827, 8834, -8835, 8836, -8835, 8838, -8839,
+ 8840, -8839, 8842, -8843, 8876, 8866, 8877, -8873, 8879, 8875, 8880, -8881,
+ 8928, -8829, 8930, -8850, 8932, -8933, 8938, -8883, 8942, -8943, 9001, -12297,
+ 9003, -9004, 9312, -50, 9321, -9322, 9352, -50, 9361, -9362, 9398, -66,
+ 9424, -98, 9450, 48, 9451, -9452, 10972, 10973, 10974, -10975, 11388, 106,
+ 11389, 86, 11390, -11391, 11503, 0, 11506, -11507, 11631, 11617, 11632, -11633,
+ 11647, 0, 11648, -11649, 11744, 0, 11776, -11777, 11935, 27597, 11936, -11937,
+ 12019, 40863, 12020, -12021, 12032, 19968, 12033, 20008, 12034, 20022, 12035, 20031,
+ 12036, 20057, 12037, 20101, 12038, 20108, 12039, 20128, 12040, 20154, 12041, 20799,
+ 12042, 20837, 12043, 20843, 12044, 20866, 12045, 20886, 12046, 20907, 12047, 20960,
+ 12048, 20981, 12049, 20992, 12050, 21147, 12051, 21241, 12052, 21269, 12053, 21274,
+ 12054, 21304, 12055, 21313, 12056, 21340, 12057, 21353, 12058, 21378, 12059, 21430,
+ 12060, 21448, 12061, 21475, 12062, 22231, 12063, 22303, 12064, 22763, 12065, 22786,
+ 12066, 22794, 12067, 22805, 12068, 22823, 12069, 22899, 12070, 23376, 12071, 23424,
+ 12072, 23544, 12073, 23567, 12074, 23586, 12075, 23608, 12076, 23662, 12077, 23665,
+ 12078, 24027, 12079, 24037, 12080, 24049, 12081, 24062, 12082, 24178, 12083, 24186,
+ 12084, 24191, 12085, 24308, 12086, 24318, 12087, 24331, 12088, 24339, 12089, 24400,
+ 12090, 24417, 12091, 24435, 12092, 24515, 12093, 25096, 12094, 25142, 12095, 25163,
+ 12096, 25903, 12097, 25908, 12098, 25991, 12099, 26007, 12100, 26020, 12101, 26041,
+ 12102, 26080, 12103, 26085, 12104, 26352, 12105, 26376, 12106, 26408, 12107, 27424,
+ 12108, 27490, 12109, 27513, 12110, 27571, 12111, 27595, 12112, 27604, 12113, 27611,
+ 12114, 27663, 12115, 27668, 12116, 27700, 12117, 28779, 12118, 29226, 12119, 29238,
+ 12120, 29243, 12121, 29247, 12122, 29255, 12123, 29273, 12124, 29275, 12125, 29356,
+ 12126, 29572, 12127, 29577, 12128, 29916, 12129, 29926, 12130, 29976, 12131, 29983,
+ 12132, 29992, 12133, 30000, 12134, 30091, 12135, 30098, 12136, 30326, 12137, 30333,
+ 12138, 30382, 12139, 30399, 12140, 30446, 12141, 30683, 12142, 30690, 12143, 30707,
+ 12144, 31034, 12145, 31160, 12146, 31166, 12147, 31348, 12148, 31435, 12149, 31481,
+ 12150, 31859, 12151, 31992, 12152, 32566, 12153, 32593, 12154, 32650, 12155, 32701,
+ 12156, 32769, 12157, 32780, 12158, 32786, 12159, 32819, 12160, 32895, 12161, 32905,
+ 12162, 33251, 12163, 33258, 12164, 33267, 12165, 33276, 12166, 33292, 12167, 33307,
+ 12168, 33311, 12169, 33390, 12170, 33394, 12171, 33400, 12172, 34381, 12173, 34411,
+ 12174, 34880, 12175, 34892, 12176, 34915, 12177, 35198, 12178, 35211, 12179, 35282,
+ 12180, 35328, 12181, 35895, 12182, 35910, 12183, 35925, 12184, 35960, 12185, 35997,
+ 12186, 36196, 12187, 36208, 12188, 36275, 12189, 36523, 12190, 36554, 12191, 36763,
+ 12192, 36784, 12193, 36789, 12194, 37009, 12195, 37193, 12196, 37318, 12197, 37324,
+ 12198, 37329, 12199, 38263, 12200, 38272, 12201, 38428, 12202, 38582, 12203, 38585,
+ 12204, 38632, 12205, 38737, 12206, 38750, 12207, 38754, 12208, 38761, 12209, 38859,
+ 12210, 38893, 12211, 38899, 12212, 38913, 12213, 39080, 12214, 39131, 12215, 39135,
+ 12216, 39318, 12217, 39321, 12218, 39340, 12219, 39592, 12220, 39640, 12221, 39647,
+ 12222, 39717, 12223, 39727, 12224, 39730, 12225, 39740, 12226, 39770, 12227, 40165,
+ 12228, 40565, 12229, 40575, 12230, 40613, 12231, 40635, 12232, 40643, 12233, 40653,
+ 12234, 40657, 12235, 40697, 12236, 40701, 12237, 40718, 12238, 40723, 12239, 40736,
+ 12240, 40763, 12241, 40778, 12242, 40786, 12243, 40845, 12244, 40860, 12245, 40864,
+ 12246, -12247, 12288, 32, 12289, -12290, 12330, 0, 12336, -12337, 12342, 12306,
+ 12343, 12343, 12344, 21313, 12345, -21317, 12347, -12348, 12364, 2097154, 12388, 2097152,
+ 12395, -12396, 12400, 12399, 12402, 12402, 12405, 12405, 12408, 12408, 12411, 12411,
+ 12414, -12415, 12436, 12358, 12437, -12438, 12441, 0, 12443, 32, 12445, 2097154,
+ 12448, -12449, 12460, 2097154, 12484, 2097152, 12491, -12492, 12496, 12495, 12498, 12498,
+ 12501, 12501, 12504, 12504, 12507, 12507, 12510, -12511, 12532, 12454, 12533, -12534,
+ 12535, -12528, 12539, 12539, 12540, 0, 12541, 2097154, 12544, -12545, 12593, -4353,
+ 12595, 4522, 12596, 4354, 12597, -4525, 12599, -4356, 12602, -4529, 12608, 4378,
+ 12609, -4359, 12612, 4385, 12613, -4362, 12623, -4450, 12644, 4448, 12645, -4373,
+ 12647, -4552, 12649, 4556, 12650, 4558, 12651, 4563, 12652, 4567, 12653, 4569,
+ 12654, 4380, 12655, 4573, 12656, 4575, 12657, -4382, 12659, 4384, 12660, -4387,
+ 12662, 4391, 12663, 4393, 12664, -4396, 12669, 4402, 12670, 4406, 12671, 4416,
+ 12672, 4423, 12673, 4428, 12674, -4594, 12676, -4440, 12679, -4485, 12681, 4488,
+ 12682, -4498, 12684, 4500, 12685, 4510, 12686, 4513, 12687, -12688, 12690, 19968,
+ 12691, 20108, 12692, 19977, 12693, 22235, 12694, 19978, 12695, 20013, 12696, 19979,
+ 12697, 30002, 12698, 20057, 12699, 19993, 12700, 19969, 12701, 22825, 12702, 22320,
+ 12703, 20154, 12704, -12705, 12868, 21839, 12869, 24188, 12870, 25991, 12871, 31631,
+ 12872, -12873, 12896, 4352, 12897, -4355, 12899, -4358, 12902, 4361, 12903, -4364,
+ 12905, -4367, 12910, -12911, 12928, 19968, 12929, 20108, 12930, 19977, 12931, 22235,
+ 12932, 20116, 12933, 20845, 12934, 19971, 12935, 20843, 12936, 20061, 12937, 21313,
+ 12938, 26376, 12939, 28779, 12940, 27700, 12941, 26408, 12942, 37329, 12943, 22303,
+ 12944, 26085, 12945, 26666, 12946, 26377, 12947, 31038, 12948, 21517, 12949, 29305,
+ 12950, 36001, 12951, 31069, 12952, 21172, 12953, 31192, 12954, 30007, 12955, 22899,
+ 12956, 36969, 12957, 20778, 12958, 21360, 12959, 27880, 12960, 38917, 12961, 20241,
+ 12962, 20889, 12963, 27491, 12964, 19978, 12965, 20013, 12966, 19979, 12967, 24038,
+ 12968, 21491, 12969, 21307, 12970, 23447, 12971, 23398, 12972, 30435, 12973, 20225,
+ 12974, 36039, 12975, 21332, 12976, 22812, 12977, -12978, 13008, 12450, 13009, 12452,
+ 13010, 12454, 13011, 12456, 13012, -12459, 13014, 12461, 13015, 12463, 13016, 12465,
+ 13017, 12467, 13018, 12469, 13019, 12471, 13020, 12473, 13021, 12475, 13022, 12477,
+ 13023, 12479, 13024, 12481, 13025, 12484, 13026, 12486, 13027, 12488, 13028, -12491,
+ 13034, 12498, 13035, 12501, 13036, 12504, 13037, 12507, 13038, -12511, 13043, 12516,
+ 13044, 12518, 13045, -12521, 13051, -12528, 13055, -13056, 42607, 0, 42611, 42611,
+ 42612, 0, 42622, -42623, 42652, 1098, 42653, 1100, 42654, 0, 42656, -42657,
+ 42736, 0, 42738, -42739, 42864, 2097154, 42866, -42867, 42994, 67, 42995, 70,
+ 42996, 81, 42997, -42998, 43000, 294, 43001, 339, 43002, -43003, 43010, 0,
+ 43011, -43012, 43014, 0, 43015, -43016, 43019, 0, 43020, -43021, 43043, 0,
+ 43048, -43049, 43052, 0, 43053, -43054, 43136, 0, 43138, -43139, 43188, 0,
+ 43206, -43207, 43232, 0, 43250, -43251, 43263, 0, 43264, -43265, 43302, 0,
+ 43310, -43311, 43335, 0, 43348, -43349, 43392, 0, 43396, -43397, 43443, 0,
+ 43457, -43458, 43493, 0, 43494, -43495, 43561, 0, 43575, -43576, 43587, 0,
+ 43588, -43589, 43596, 0, 43598, -43599, 43643, 0, 43646, -43647, 43696, 0,
+ 43697, 43697, 43698, 0, 43701, -43702, 43703, 0, 43705, -43706, 43710, 0,
+ 43712, 43712, 43713, 0, 43714, -43715, 43755, 0, 43760, -43761, 43765, 0,
+ 43767, -43768, 43868, 42791, 43869, 43831, 43870, 619, 43871, 43858, 43872, -43873,
+ 43881, 653, 43882, -43883, 44003, 0, 44011, 44011, 44012, 0, 44014, -44015,
+ 55296, 0, 57344, -57345, 63744, 35912, 63745, 26356, 63746, 36554, 63747, 36040,
+ 63748, 28369, 63749, 20018, 63750, 21477, 63751, 40860, 63753, 22865, 63754, 37329,
+ 63755, 21895, 63756, 22856, 63757, 25078, 63758, 30313, 63759, 32645, 63760, 34367,
+ 63761, 34746, 63762, 35064, 63763, 37007, 63764, 27138, 63765, 27931, 63766, 28889,
+ 63767, 29662, 63768, 33853, 63769, 37226, 63770, 39409, 63771, 20098, 63772, 21365,
+ 63773, 27396, 63774, 29211, 63775, 34349, 63776, 40478, 63777, 23888, 63778, 28651,
+ 63779, 34253, 63780, 35172, 63781, 25289, 63782, 33240, 63783, 34847, 63784, 24266,
+ 63785, 26391, 63786, 28010, 63787, 29436, 63788, 37070, 63789, 20358, 63790, 20919,
+ 63791, 21214, 63792, 25796, 63793, 27347, 63794, 29200, 63795, 30439, 63796, 32769,
+ 63797, 34310, 63798, 34396, 63799, 36335, 63800, 38706, 63801, 39791, 63802, 40442,
+ 63803, 30860, 63804, 31103, 63805, 32160, 63806, 33737, 63807, 37636, 63808, 40575,
+ 63809, 35542, 63810, 22751, 63811, 24324, 63812, 31840, 63813, 32894, 63814, 29282,
+ 63815, 30922, 63816, 36034, 63817, 38647, 63818, 22744, 63819, 23650, 63820, 27155,
+ 63821, 28122, 63822, 28431, 63823, 32047, 63824, 32311, 63825, 38475, 63826, 21202,
+ 63827, 32907, 63828, 20956, 63829, 20940, 63830, 31260, 63831, 32190, 63832, 33777,
+ 63833, 38517, 63834, 35712, 63835, 25295, 63836, 27138, 63837, 35582, 63838, 20025,
+ 63839, 23527, 63840, 24594, 63841, 29575, 63842, 30064, 63843, 21271, 63844, 30971,
+ 63845, 20415, 63846, 24489, 63847, 19981, 63848, 27852, 63849, 25976, 63850, 32034,
+ 63851, 21443, 63852, 22622, 63853, 30465, 63854, 33865, 63855, 35498, 63856, 27578,
+ 63857, 36784, 63858, 27784, 63859, 25342, 63860, 33509, 63861, 25504, 63862, 30053,
+ 63863, 20142, 63864, 20841, 63865, 20937, 63866, 26753, 63867, 31975, 63868, 33391,
+ 63869, 35538, 63870, 37327, 63871, 21237, 63872, 21570, 63873, 22899, 63874, 24300,
+ 63875, 26053, 63876, 28670, 63877, 31018, 63878, 38317, 63879, 39530, 63880, 40599,
+ 63881, 40654, 63882, 21147, 63883, 26310, 63884, 27511, 63885, 36706, 63886, 24180,
+ 63887, 24976, 63888, 25088, 63889, 25754, 63890, 28451, 63891, 29001, 63892, 29833,
+ 63893, 31178, 63894, 32244, 63895, 32879, 63896, 36646, 63897, 34030, 63898, 36899,
+ 63899, 37706, 63900, 21015, 63901, 21155, 63902, 21693, 63903, 28872, 63904, 35010,
+ 63905, 35498, 63906, 24265, 63907, 24565, 63908, 25467, 63909, 27566, 63910, 31806,
+ 63911, 29557, 63912, 20196, 63913, 22265, 63914, 23527, 63915, 23994, 63916, 24604,
+ 63917, 29618, 63918, 29801, 63919, 32666, 63920, 32838, 63921, 37428, 63922, 38646,
+ 63923, 38728, 63924, 38936, 63925, 20363, 63926, 31150, 63927, 37300, 63928, 38584,
+ 63929, 24801, 63930, 20102, 63931, 20698, 63932, 23534, 63933, 23615, 63934, 26009,
+ 63935, 27138, 63936, 29134, 63937, 30274, 63938, 34044, 63939, 36988, 63940, 40845,
+ 63941, 26248, 63942, 38446, 63943, 21129, 63944, 26491, 63945, 26611, 63946, 27969,
+ 63947, 28316, 63948, 29705, 63949, 30041, 63950, 30827, 63951, 32016, 63952, 39006,
+ 63953, 20845, 63954, 25134, 63955, 38520, 63956, 20523, 63957, 23833, 63958, 28138,
+ 63959, 36650, 63960, 24459, 63961, 24900, 63962, 26647, 63963, 29575, 63964, 38534,
+ 63965, 21033, 63966, 21519, 63967, 23653, 63968, 26131, 63969, 26446, 63970, 26792,
+ 63971, 27877, 63972, 29702, 63973, 30178, 63974, 32633, 63975, 35023, 63976, 35041,
+ 63977, 37324, 63978, 38626, 63979, 21311, 63980, 28346, 63981, 21533, 63982, 29136,
+ 63983, 29848, 63984, 34298, 63985, 38563, 63986, 40023, 63987, 40607, 63988, 26519,
+ 63989, 28107, 63990, 33256, 63991, 31435, 63992, 31520, 63993, 31890, 63994, 29376,
+ 63995, 28825, 63996, 35672, 63997, 20160, 63998, 33590, 63999, 21050, 64000, 20999,
+ 64001, 24230, 64002, 25299, 64003, 31958, 64004, 23429, 64005, 27934, 64006, 26292,
+ 64007, 36667, 64008, 34892, 64009, 38477, 64010, 35211, 64011, 24275, 64012, 20800,
+ 64013, 21952, 64014, -64015, 64016, 22618, 64017, 64017, 64018, 26228, 64019, -64020,
+ 64021, 20958, 64022, 29482, 64023, 30410, 64024, 31036, 64025, 31070, 64026, 31077,
+ 64027, 31119, 64028, 38742, 64029, 31934, 64030, 32701, 64031, 64031, 64032, 34322,
+ 64033, 64033, 64034, 35576, 64035, -64036, 64037, 36920, 64038, 37117, 64039, -64040,
+ 64042, 39151, 64043, 39164, 64044, 39208, 64045, 40372, 64046, 37086, 64047, 38583,
+ 64048, 20398, 64049, 20711, 64050, 20813, 64051, 21193, 64052, 21220, 64053, 21329,
+ 64054, 21917, 64055, 22022, 64056, 22120, 64057, 22592, 64058, 22696, 64059, 23652,
+ 64060, 23662, 64061, 24724, 64062, 24936, 64063, 24974, 64064, 25074, 64065, 25935,
+ 64066, 26082, 64067, 26257, 64068, 26757, 64069, 28023, 64070, 28186, 64071, 28450,
+ 64072, 29038, 64073, 29227, 64074, 29730, 64075, 30865, 64076, 31038, 64077, 31049,
+ 64078, 31048, 64079, 31056, 64080, 31062, 64081, 31069, 64082, -31118, 64084, 31296,
+ 64085, 31361, 64086, 31680, 64087, 32244, 64088, 32265, 64089, 32321, 64090, 32626,
+ 64091, 32773, 64092, 33261, 64093, 33401, 64095, 33879, 64096, 35088, 64097, 35222,
+ 64098, 35585, 64099, 35641, 64100, 36051, 64101, 36104, 64102, 36790, 64103, 36920,
+ 64104, 38627, 64105, 38911, 64106, 38971, 64107, 24693, 64108, 148206, 64109, 33304,
+ 64110, -64111, 64112, 20006, 64113, 20917, 64114, 20840, 64115, 20352, 64116, 20805,
+ 64117, 20864, 64118, 21191, 64119, 21242, 64120, 21917, 64121, 21845, 64122, 21913,
+ 64123, 21986, 64124, 22618, 64125, 22707, 64126, 22852, 64127, 22868, 64128, 23138,
+ 64129, 23336, 64130, 24274, 64131, 24281, 64132, 24425, 64133, 24493, 64134, 24792,
+ 64135, 24910, 64136, 24840, 64137, 24974, 64138, 24928, 64139, 25074, 64140, 25140,
+ 64141, 25540, 64142, 25628, 64143, 25682, 64144, 25942, 64145, 26228, 64146, 26391,
+ 64147, 26395, 64148, 26454, 64149, 27513, 64150, 27578, 64151, 27969, 64152, 28379,
+ 64153, 28363, 64154, 28450, 64155, 28702, 64156, 29038, 64157, 30631, 64158, 29237,
+ 64159, 29359, 64160, 29482, 64161, 29809, 64162, 29958, 64163, 30011, 64164, 30237,
+ 64165, 30239, 64166, 30410, 64167, 30427, 64168, 30452, 64169, 30538, 64170, 30528,
+ 64171, 30924, 64172, 31409, 64173, 31680, 64174, 31867, 64175, 32091, 64176, 32244,
+ 64177, 32574, 64178, 32773, 64179, 33618, 64180, 33775, 64181, 34681, 64182, 35137,
+ 64183, 35206, 64184, 35222, 64185, 35519, 64186, 35576, 64187, 35531, 64188, 35585,
+ 64189, 35582, 64190, 35565, 64191, 35641, 64192, 35722, 64193, 36104, 64194, 36664,
+ 64195, 36978, 64196, 37273, 64197, 37494, 64198, 38524, 64199, 38627, 64200, 38742,
+ 64201, 38875, 64202, 38911, 64203, 38923, 64204, 38971, 64205, 39698, 64206, 40860,
+ 64207, 141386, 64208, 141380, 64209, 144341, 64210, 15261, 64211, 16408, 64212, 16441,
+ 64213, 152137, 64214, 154832, 64215, 163539, 64216, 40771, 64217, 40846, 64218, -64219,
+ 64285, 1497, 64286, 0, 64287, 1522, 64288, 1506, 64289, 1488, 64290, -1492,
+ 64292, -1500, 64295, 1512, 64296, 1514, 64297, 43, 64298, 1513, 64302, 1488,
+ 64305, -1490, 64311, 64311, 64312, -1497, 64317, 64317, 64318, 1502, 64319, 64319,
+ 64320, -1505, 64322, 64322, 64323, -1508, 64325, 64325, 64326, -1511, 64331, 1493,
+ 64332, 1489, 64333, 1499, 64334, 1508, 64335, 64335, 64336, 1649, 64338, 1659,
+ 64342, 1662, 64346, 1664, 64350, 1658, 64354, 1663, 64358, 1657, 64362, 1700,
+ 64366, 1702, 64370, 1668, 64374, 1667, 64378, 1670, 64382, 1671, 64386, 1677,
+ 64388, 1676, 64390, 1678, 64392, 1672, 64394, 1688, 64396, 1681, 64398, 1705,
+ 64402, 1711, 64406, 1715, 64410, 1713, 64414, 1722, 64416, 1723, 64420, 1749,
+ 64422, 1729, 64426, 1726, 64430, 1746, 64434, -64435, 64467, 1709, 64471, 1735,
+ 64473, 1734, 64475, 1736, 64477, 1655, 64478, 1739, 64480, 1733, 64482, 1737,
+ 64484, 1744, 64488, 1609, 64490, -64491, 64508, 1740, 64512, -64513, 64603, -1585,
+ 64605, 1609, 64606, 32, 64612, -64613, 64656, 1609, 64657, -64658, 64729, 1607,
+ 64730, -64731, 64754, 1600, 64757, -64758, 64828, 1575, 64830, -64831, 65024, 0,
+ 65040, 44, 65041, -12290, 65043, -59, 65045, 33, 65046, 63, 65047, -12311,
+ 65049, 8230, 65050, -65051, 65056, 0, 65072, 8229, 65073, 8212, 65074, 8211,
+ 65075, 95, 65077, -41, 65079, 123, 65080, 125, 65081, -12309, 65083, -12305,
+ 65085, -12299, 65087, -12297, 65089, -12301, 65093, -65094, 65095, 91, 65096, 93,
+ 65097, 32, 65101, 95, 65104, 44, 65105, 12289, 65106, 46, 65107, 65107,
+ 65108, 59, 65109, 58, 65110, 63, 65111, 33, 65112, 8212, 65113, -41,
+ 65115, 123, 65116, 125, 65117, -12309, 65119, 35, 65120, 38, 65121, -43,
+ 65123, 45, 65124, 60, 65125, 62, 65126, 61, 65127, 65127, 65128, 92,
+ 65129, -37, 65131, 64, 65132, -65133, 65136, 32, 65137, 1600, 65138, 32,
+ 65139, 65139, 65140, 32, 65141, 65141, 65142, 32, 65143, 1600, 65144, 32,
+ 65145, 1600, 65146, 32, 65147, 1600, 65148, 32, 65149, 1600, 65150, 32,
+ 65151, 1600, 65152, 1569, 65153, 1575, 65157, 1608, 65159, 1575, 65161, 1610,
+ 65165, 1575, 65167, 1576, 65171, 1577, 65173, 1578, 65177, 1579, 65181, 1580,
+ 65185, 1581, 65189, 1582, 65193, 1583, 65195, 1584, 65197, 1585, 65199, 1586,
+ 65201, 1587, 65205, 1588, 65209, 1589, 65213, 1590, 65217, 1591, 65221, 1592,
+ 65225, 1593, 65229, 1594, 65233, 1601, 65237, 1602, 65241, 1603, 65245, 1604,
+ 65249, 1605, 65253, 1606, 65257, 1607, 65261, 1608, 65263, 1609, 65265, 1610,
+ 65269, -65270, 65279, 0, 65280, 65280, 65281, -34, 65375, -10630, 65377, 12290,
+ 65378, -12301, 65380, 12289, 65381, 12539, 65382, 12530, 65383, 12449, 65384, 12451,
+ 65385, 12453, 65386, 12455, 65387, 12457, 65388, 12515, 65389, 12517, 65390, 12519,
+ 65391, 12483, 65392, 65392, 65393, 12450, 65394, 12452, 65395, 12454, 65396, 12456,
+ 65397, -12459, 65399, 12461, 65400, 12463, 65401, 12465, 65402, 12467, 65403, 12469,
+ 65404, 12471, 65405, 12473, 65406, 12475, 65407, 12477, 65408, 12479, 65409, 12481,
+ 65410, 12484, 65411, 12486, 65412, 12488, 65413, -12491, 65419, 12498, 65420, 12501,
+ 65421, 12504, 65422, 12507, 65423, -12511, 65428, 12516, 65429, 12518, 65430, -12521,
+ 65436, 12527, 65437, 12531, 65438, -65439, 65440, 4448, 65441, -4353, 65443, 4522,
+ 65444, 4354, 65445, -4525, 65447, -4356, 65450, -4529, 65456, 4378, 65457, -4359,
+ 65460, 4385, 65461, -4362, 65471, -65472, 65474, -4450, 65480, -65481, 65482, -4456,
+ 65488, -65489, 65490, -4462, 65496, -65497, 65498, -4468, 65501, -65502, 65504, -163,
+ 65506, 172, 65507, 32, 65508, 166, 65509, 165, 65510, 8361, 65511, 65511,
+ 65512, 9474, 65513, -8593, 65517, 9632, 65518, 9675, 65519, -65520, 65529, 0,
+ 65532, -65533, 66045, 0, 66046, -66047, 66272, 0, 66273, -66274, 66422, 0,
+ 66427, -66428, 67457, -721, 67459, 230, 67460, 665, 67461, 595, 67462, 67462,
+ 67463, 675, 67464, 43878, 67465, 677, 67466, 676, 67467, -599, 67469, 7569,
+ 67470, 600, 67471, 606, 67472, 681, 67473, 612, 67474, 610, 67475, 608,
+ 67476, 667, 67477, 295, 67478, 668, 67479, 615, 67480, 644, 67481, -683,
+ 67483, 620, 67484, 122628, 67485, 42894, 67486, 622, 67487, 122629, 67488, 654,
+ 67489, 122630, 67490, 248, 67491, -631, 67493, 113, 67494, 634, 67495, 122632,
+ 67496, -638, 67498, 640, 67499, 680, 67500, 678, 67501, 43879, 67502, 679,
+ 67503, 648, 67504, 11377, 67505, 67505, 67506, 655, 67507, -674, 67509, 664,
+ 67510, -449, 67513, 122634, 67514, 122654, 67515, -67516, 68097, 0, 68100, 68100,
+ 68101, 0, 68103, -68104, 68108, 0, 68112, -68113, 68152, 0, 68155, -68156,
+ 68159, 0, 68160, -68161, 68325, 0, 68327, -68328, 68900, 0, 68904, -68905,
+ 69291, 0, 69293, -69294, 69373, 0, 69376, -69377, 69446, 0, 69457, -69458,
+ 69506, 0, 69510, -69511, 69632, 0, 69635, -69636, 69688, 0, 69703, -69704,
+ 69744, 0, 69745, -69746, 69747, 0, 69749, -69750, 69759, 0, 69763, -69764,
+ 69786, 2097154, 69790, -69791, 69803, 69797, 69804, -69805, 69808, 0, 69819, -69820,
+ 69821, 0, 69822, -69823, 69826, 0, 69827, -69828, 69837, 0, 69838, -69839,
+ 69888, 0, 69891, -69892, 69927, 0, 69941, -69942, 69957, 0, 69959, -69960,
+ 70003, 0, 70004, -70005, 70016, 0, 70019, -70020, 70067, 0, 70081, -70082,
+ 70089, 0, 70093, 70093, 70094, 0, 70096, -70097, 70188, 0, 70200, -70201,
+ 70206, 0, 70207, -70208, 70209, 0, 70210, -70211, 70367, 0, 70379, -70380,
+ 70400, 0, 70404, -70405, 70459, 0, 70461, 70461, 70462, 0, 70469, -70470,
+ 70471, 0, 70473, -70474, 70475, 0, 70478, -70479, 70487, 0, 70488, -70489,
+ 70498, 0, 70500, -70501, 70502, 0, 70509, -70510, 70512, 0, 70517, -70518,
+ 70709, 0, 70727, -70728, 70750, 0, 70751, -70752, 70832, 0, 70852, -70853,
+ 71087, 0, 71094, -71095, 71096, 0, 71105, -71106, 71132, 0, 71134, -71135,
+ 71216, 0, 71233, -71234, 71339, 0, 71352, -71353, 71453, 0, 71468, -71469,
+ 71724, 0, 71739, -71740, 71984, 0, 71990, 71990, 71991, 0, 71993, -71994,
+ 71995, 0, 71999, 71999, 72000, 0, 72001, 72001, 72002, 0, 72004, -72005,
+ 72145, 0, 72152, -72153, 72154, 0, 72161, -72162, 72164, 0, 72165, -72166,
+ 72193, 0, 72203, -72204, 72243, 0, 72250, 72250, 72251, 0, 72255, -72256,
+ 72263, 0, 72264, -72265, 72273, 0, 72284, -72285, 72330, 0, 72346, -72347,
+ 72751, 0, 72759, 72759, 72760, 0, 72768, -72769, 72850, 0, 72872, 72872,
+ 72873, 0, 72887, -72888, 73009, 0, 73015, -73016, 73018, 0, 73019, 73019,
+ 73020, 0, 73022, 73022, 73023, 0, 73030, 73030, 73031, 0, 73032, -73033,
+ 73098, 0, 73103, 73103, 73104, 0, 73106, 73106, 73107, 0, 73112, -73113,
+ 73459, 0, 73463, -73464, 73472, 0, 73474, 73474, 73475, 0, 73476, -73477,
+ 73524, 0, 73531, -73532, 73534, 0, 73539, -73540, 78896, 0, 78913, -78914,
+ 78919, 0, 78934, -78935, 92912, 0, 92917, -92918, 92976, 0, 92983, -92984,
+ 94031, 0, 94032, 94032, 94033, 0, 94088, -94089, 94095, 0, 94099, -94100,
+ 94180, 0, 94181, -94182, 94192, 0, 94194, -94195, 113821, 0, 113823, 113823,
+ 113824, 0, 113828, -113829, 118528, 0, 118574, -118575, 118576, 0, 118599, -118600,
+ 119134, -119128, 119136, 119128, 119141, 0, 119146, -119147, 119149, 0, 119171, -119172,
+ 119173, 0, 119180, -119181, 119210, 0, 119214, -119215, 119227, -119226, 119229, -119226,
+ 119231, -119226, 119233, -119234, 119362, 0, 119365, -119366, 119808, -66, 119834, -98,
+ 119860, -66, 119886, -98, 119893, 119893, 119894, -106, 119912, -66, 119938, -98,
+ 119964, 65, 119965, 119965, 119966, -68, 119968, -119969, 119970, 71, 119971, -119972,
+ 119973, -75, 119975, -119976, 119977, -79, 119981, 119981, 119982, -84, 119990, -98,
+ 119994, 119994, 119995, 102, 119996, 119996, 119997, -105, 120004, 120004, 120005, -113,
+ 120016, -66, 120042, -98, 120068, -66, 120070, 120070, 120071, -69, 120075, -120076,
+ 120077, -75, 120085, 120085, 120086, -84, 120093, 120093, 120094, -98, 120120, -66,
+ 120122, 120122, 120123, -69, 120127, 120127, 120128, -74, 120133, 120133, 120134, 79,
+ 120135, -120136, 120138, -84, 120145, 120145, 120146, -98, 120172, -66, 120198, -98,
+ 120224, -66, 120250, -98, 120276, -66, 120302, -98, 120328, -66, 120354, -98,
+ 120380, -66, 120406, -98, 120432, -66, 120458, -98, 120484, 305, 120485, 567,
+ 120486, -120487, 120488, -914, 120505, 920, 120506, -932, 120513, 8711, 120514, -946,
+ 120539, 8706, 120540, 949, 120541, 952, 120542, 954, 120543, 966, 120544, 961,
+ 120545, 960, 120546, -914, 120563, 920, 120564, -932, 120571, 8711, 120572, -946,
+ 120597, 8706, 120598, 949, 120599, 952, 120600, 954, 120601, 966, 120602, 961,
+ 120603, 960, 120604, -914, 120621, 920, 120622, -932, 120629, 8711, 120630, -946,
+ 120655, 8706, 120656, 949, 120657, 952, 120658, 954, 120659, 966, 120660, 961,
+ 120661, 960, 120662, -914, 120679, 920, 120680, -932, 120687, 8711, 120688, -946,
+ 120713, 8706, 120714, 949, 120715, 952, 120716, 954, 120717, 966, 120718, 961,
+ 120719, 960, 120720, -914, 120737, 920, 120738, -932, 120745, 8711, 120746, -946,
+ 120771, 8706, 120772, 949, 120773, 952, 120774, 954, 120775, 966, 120776, 961,
+ 120777, 960, 120778, -989, 120780, -120781, 120782, -49, 120792, -49, 120802, -49,
+ 120812, -49, 120822, -49, 120832, -120833, 121344, 0, 121399, -121400, 121403, 0,
+ 121453, -121454, 121461, 0, 121462, -121463, 121476, 0, 121477, -121478, 121499, 0,
+ 121504, 121504, 121505, 0, 121520, -121521, 122880, 0, 122887, 122887, 122888, 0,
+ 122905, -122906, 122907, 0, 122914, 122914, 122915, 0, 122917, 122917, 122918, 0,
+ 122923, -122924, 122928, -1073, 122937, -1083, 122940, -1087, 122951, 1099, 122952, -1102,
+ 122954, 42633, 122955, 1241, 122956, 1110, 122957, 1112, 122958, 1257, 122959, 1199,
+ 122960, 1231, 122961, -1073, 122970, -1083, 122972, -1087, 122974, 1089, 122975, -1092,
+ 122981, -1099, 122983, 1169, 122984, 1110, 122985, 1109, 122986, 1119, 122987, 1195,
+ 122988, 42577, 122989, 1201, 122990, -122991, 123023, 0, 123024, -123025, 123184, 0,
+ 123191, -123192, 123566, 0, 123567, -123568, 123628, 0, 123632, -123633, 124140, 0,
+ 124144, -124145, 125136, 0, 125143, -125144, 125252, 0, 125259, -125260, 126464, -1576,
+ 126466, 1580, 126467, 1583, 126468, 126468, 126469, 1608, 126470, 1586, 126471, 1581,
+ 126472, 1591, 126473, 1610, 126474, -1604, 126478, 1587, 126479, 1593, 126480, 1601,
+ 126481, 1589, 126482, 1602, 126483, 1585, 126484, 1588, 126485, -1579, 126487, 1582,
+ 126488, 1584, 126489, 1590, 126490, 1592, 126491, 1594, 126492, 1646, 126493, 1722,
+ 126494, 1697, 126495, 1647, 126496, 126496, 126497, 1576, 126498, 1580, 126499, 126499,
+ 126500, 1607, 126501, -126502, 126503, 1581, 126504, 126504, 126505, 1610, 126506, -1604,
+ 126510, 1587, 126511, 1593, 126512, 1601, 126513, 1589, 126514, 1602, 126515, 126515,
+ 126516, 1588, 126517, -1579, 126519, 1582, 126520, 126520, 126521, 1590, 126522, 126522,
+ 126523, 1594, 126524, -126525, 126530, 1580, 126531, -126532, 126535, 1581, 126536, 126536,
+ 126537, 1610, 126538, 126538, 126539, 1604, 126540, 126540, 126541, 1606, 126542, 1587,
+ 126543, 1593, 126544, 126544, 126545, 1589, 126546, 1602, 126547, 126547, 126548, 1588,
+ 126549, -126550, 126551, 1582, 126552, 126552, 126553, 1590, 126554, 126554, 126555, 1594,
+ 126556, 126556, 126557, 1722, 126558, 126558, 126559, 1647, 126560, 126560, 126561, 1576,
+ 126562, 1580, 126563, 126563, 126564, 1607, 126565, -126566, 126567, 1581, 126568, 1591,
+ 126569, 1610, 126570, 1603, 126571, 126571, 126572, -1606, 126574, 1587, 126575, 1593,
+ 126576, 1601, 126577, 1589, 126578, 1602, 126579, 126579, 126580, 1588, 126581, -1579,
+ 126583, 1582, 126584, 126584, 126585, 1590, 126586, 1592, 126587, 1594, 126588, 1646,
+ 126589, 126589, 126590, 1697, 126591, 126591, 126592, -1576, 126594, 1580, 126595, 1583,
+ 126596, -1608, 126598, 1586, 126599, 1581, 126600, 1591, 126601, 1610, 126602, 126602,
+ 126603, -1605, 126606, 1587, 126607, 1593, 126608, 1601, 126609, 1589, 126610, 1602,
+ 126611, 1585, 126612, 1588, 126613, -1579, 126615, 1582, 126616, 1584, 126617, 1590,
+ 126618, 1592, 126619, 1594, 126620, -126621, 126625, 1576, 126626, 1580, 126627, 1583,
+ 126628, 126628, 126629, 1608, 126630, 1586, 126631, 1581, 126632, 1591, 126633, 1610,
+ 126634, 126634, 126635, -1605, 126638, 1587, 126639, 1593, 126640, 1601, 126641, 1589,
+ 126642, 1602, 126643, 1585, 126644, 1588, 126645, -1579, 126647, 1582, 126648, 1584,
+ 126649, 1590, 126650, 1592, 126651, 1594, 126652, -126653, 127232, 48, 127234, -50,
+ 127243, -127244, 127275, 67, 127276, 82, 127277, -127278, 127280, -66, 127306, -127307,
+ 127490, 12469, 127491, -127492, 127504, 25163, 127505, 23383, 127506, 21452, 127507, 12486,
+ 127508, 20108, 127509, 22810, 127510, 35299, 127511, 22825, 127512, 20132, 127513, 26144,
+ 127514, 28961, 127515, 26009, 127516, 21069, 127517, 24460, 127518, 20877, 127519, 26032,
+ 127520, 21021, 127521, 32066, 127522, 29983, 127523, 36009, 127524, 22768, 127525, 21561,
+ 127526, 28436, 127527, 25237, 127528, 25429, 127529, 19968, 127530, 19977, 127531, 36938,
+ 127532, 24038, 127533, 20013, 127534, 21491, 127535, 25351, 127536, 36208, 127537, 25171,
+ 127538, 31105, 127539, 31354, 127540, 21512, 127541, 28288, 127542, 26377, 127543, 26376,
+ 127544, 30003, 127545, 21106, 127546, 21942, 127547, 37197, 127548, -127549, 127568, 24471,
+ 127569, 21487, 127570, -127571, 130032, -49, 130042, -130043, 194560, 20029, 194561, 20024,
+ 194562, 20033, 194563, 131362, 194564, 20320, 194565, 20398, 194566, 20411, 194567, 20482,
+ 194568, 20602, 194569, 20633, 194570, 20711, 194571, 20687, 194572, 13470, 194573, 132666,
+ 194574, 20813, 194575, 20820, 194576, 20836, 194577, 20855, 194578, 132380, 194579, 13497,
+ 194580, 20839, 194581, 20877, 194582, 132427, 194583, 20887, 194584, 20900, 194585, 20172,
+ 194586, 20908, 194587, 20917, 194588, 168415, 194589, 20981, 194590, 20995, 194591, 13535,
+ 194592, 21051, 194593, 21062, 194594, 21106, 194595, 21111, 194596, 13589, 194597, 21191,
+ 194598, 21193, 194599, 21220, 194600, 21242, 194601, -21254, 194603, 21271, 194604, 21321,
+ 194605, 21329, 194606, 21338, 194607, 21363, 194608, 21373, 194609, 21375, 194612, 133676,
+ 194613, 28784, 194614, 21450, 194615, 21471, 194616, 133987, 194617, 21483, 194618, 21489,
+ 194619, 21510, 194620, 21662, 194621, 21560, 194622, 21576, 194623, 21608, 194624, 21666,
+ 194625, 21750, 194626, 21776, 194627, 21843, 194628, 21859, 194629, 21892, 194631, 21913,
+ 194632, 21931, 194633, 21939, 194634, 21954, 194635, 22294, 194636, 22022, 194637, 22295,
+ 194638, 22097, 194639, 22132, 194640, 20999, 194641, 22766, 194642, 22478, 194643, 22516,
+ 194644, 22541, 194645, 22411, 194646, 22578, 194647, 22577, 194648, 22700, 194649, 136420,
+ 194650, 22770, 194651, 22775, 194652, 22790, 194653, 22810, 194654, 22818, 194655, 22882,
+ 194656, 136872, 194657, 136938, 194658, 23020, 194659, 23067, 194660, 23079, 194661, 23000,
+ 194662, 23142, 194663, 14062, 194664, 14076, 194665, 23304, 194666, 23358, 194668, 137672,
+ 194669, 23491, 194670, 23512, 194671, 23527, 194672, 23539, 194673, 138008, 194674, 23551,
+ 194675, 23558, 194676, 24403, 194677, 23586, 194678, 14209, 194679, 23648, 194680, 23662,
+ 194681, 23744, 194682, 23693, 194683, 138724, 194684, 23875, 194685, 138726, 194686, 23918,
+ 194687, 23915, 194688, 23932, 194689, -24034, 194691, 14383, 194692, 24061, 194693, 24104,
+ 194694, 24125, 194695, 24169, 194696, 14434, 194697, 139651, 194698, 14460, 194699, 24240,
+ 194700, 24243, 194701, 24246, 194702, 24266, 194703, 172946, 194704, 24318, 194705, 140081,
+ 194707, 33281, 194708, 24354, 194710, 14535, 194711, 144056, 194712, 156122, 194713, 24418,
+ 194714, 24427, 194715, 14563, 194716, 24474, 194717, 24525, 194718, 24535, 194719, 24569,
+ 194720, 24705, 194721, 14650, 194722, 14620, 194723, 24724, 194724, 141012, 194725, 24775,
+ 194726, 24904, 194727, 24908, 194728, 24910, 194729, 24908, 194730, 24954, 194731, 24974,
+ 194732, 25010, 194733, 24996, 194734, 25007, 194735, 25054, 194736, 25074, 194737, 25078,
+ 194738, 25104, 194739, 25115, 194740, 25181, 194741, 25265, 194742, 25300, 194743, 25424,
+ 194744, 142092, 194745, 25405, 194746, 25340, 194747, 25448, 194748, 25475, 194749, 25572,
+ 194750, 142321, 194751, 25634, 194752, 25541, 194753, 25513, 194754, 14894, 194755, 25705,
+ 194756, 25726, 194757, 25757, 194758, 25719, 194759, 14956, 194760, 25935, 194761, 25964,
+ 194762, 143370, 194763, 26083, 194764, 26360, 194765, 26185, 194766, 15129, 194767, 26257,
+ 194768, 15112, 194769, 15076, 194770, 20882, 194771, 20885, 194772, 26368, 194773, 26268,
+ 194774, 32941, 194775, 17369, 194776, 26391, 194777, 26395, 194778, 26401, 194779, 26462,
+ 194780, 26451, 194781, 144323, 194782, 15177, 194783, 26618, 194784, 26501, 194785, 26706,
+ 194786, 26757, 194787, 144493, 194788, 26766, 194789, 26655, 194790, 26900, 194791, 15261,
+ 194792, 26946, 194793, 27043, 194794, 27114, 194795, 27304, 194796, 145059, 194797, 27355,
+ 194798, 15384, 194799, 27425, 194800, 145575, 194801, 27476, 194802, 15438, 194803, 27506,
+ 194804, 27551, 194805, -27579, 194807, 146061, 194808, 138507, 194809, 146170, 194810, 27726,
+ 194811, 146620, 194812, 27839, 194813, 27853, 194814, 27751, 194815, 27926, 194816, 27966,
+ 194817, 28023, 194818, 27969, 194819, 28009, 194820, 28024, 194821, 28037, 194822, 146718,
+ 194823, 27956, 194824, 28207, 194825, 28270, 194826, 15667, 194827, 28363, 194828, 28359,
+ 194829, 147153, 194830, 28153, 194831, 28526, 194832, 147294, 194833, 147342, 194834, 28614,
+ 194835, 28729, 194836, 28702, 194837, 28699, 194838, 15766, 194839, 28746, 194840, 28797,
+ 194841, 28791, 194842, 28845, 194843, 132389, 194844, 28997, 194845, 148067, 194846, 29084,
+ 194847, 148395, 194848, 29224, 194849, 29237, 194850, 29264, 194851, 149000, 194852, 29312,
+ 194853, 29333, 194854, 149301, 194855, 149524, 194856, 29562, 194857, 29579, 194858, 16044,
+ 194859, 29605, 194860, 16056, 194862, 29767, 194863, 29788, 194864, 29809, 194865, 29829,
+ 194866, 29898, 194867, 16155, 194868, 29988, 194869, 150582, 194870, 30014, 194871, 150674,
+ 194872, 30064, 194873, 139679, 194874, 30224, 194875, 151457, 194876, 151480, 194877, 151620,
+ 194878, 16380, 194879, 16392, 194880, 30452, 194881, 151795, 194882, 151794, 194883, 151833,
+ 194884, 151859, 194885, -30495, 194887, 30495, 194888, 30538, 194889, 16441, 194890, 30603,
+ 194891, 16454, 194892, 16534, 194893, 152605, 194894, 30798, 194895, 30860, 194896, 30924,
+ 194897, 16611, 194898, 153126, 194899, 31062, 194900, 153242, 194901, 153285, 194902, 31119,
+ 194903, 31211, 194904, 16687, 194905, 31296, 194906, 31306, 194907, 31311, 194908, 153980,
+ 194909, 154279, 194911, 31470, 194912, 16898, 194913, 154539, 194914, 31686, 194915, 31689,
+ 194916, 16935, 194917, 154752, 194918, 31954, 194919, 17056, 194920, 31976, 194921, 31971,
+ 194922, 32000, 194923, 155526, 194924, 32099, 194925, 17153, 194926, 32199, 194927, 32258,
+ 194928, 32325, 194929, 17204, 194930, 156200, 194931, 156231, 194932, 17241, 194933, 156377,
+ 194934, 32634, 194935, 156478, 194936, 32661, 194937, 32762, 194938, 32773, 194939, 156890,
+ 194940, 156963, 194941, 32864, 194942, 157096, 194943, 32880, 194944, 144223, 194945, 17365,
+ 194946, 32946, 194947, 33027, 194948, 17419, 194949, 33086, 194950, 23221, 194951, 157607,
+ 194952, 157621, 194953, 144275, 194954, 144284, 194955, 33281, 194956, 33284, 194957, 36766,
+ 194958, 17515, 194959, 33425, 194960, 33419, 194961, 33437, 194962, 21171, 194963, 33457,
+ 194964, 33459, 194965, 33469, 194966, 33510, 194967, 158524, 194968, 33509, 194969, 33565,
+ 194970, 33635, 194971, 33709, 194972, 33571, 194973, 33725, 194974, 33767, 194975, 33879,
+ 194976, 33619, 194977, 33738, 194978, 33740, 194979, 33756, 194980, 158774, 194981, 159083,
+ 194982, 158933, 194983, 17707, 194984, 34033, 194985, 34035, 194986, 34070, 194987, 160714,
+ 194988, 34148, 194989, 159532, 194990, 17757, 194991, 17761, 194992, 159665, 194993, 159954,
+ 194994, 17771, 194995, 34384, 194996, 34396, 194997, 34407, 194998, 34409, 194999, 34473,
+ 195000, 34440, 195001, 34574, 195002, 34530, 195003, 34681, 195004, 34600, 195005, 34667,
+ 195006, 34694, 195007, 17879, 195008, 34785, 195009, 34817, 195010, 17913, 195011, 34912,
+ 195012, 34915, 195013, 161383, 195014, 35031, 195015, 35038, 195016, 17973, 195017, 35066,
+ 195018, 13499, 195019, 161966, 195020, 162150, 195021, 18110, 195022, 18119, 195023, 35488,
+ 195024, 35565, 195025, 35722, 195026, 35925, 195027, 162984, 195028, 36011, 195029, 36033,
+ 195030, 36123, 195031, 36215, 195032, 163631, 195033, 133124, 195034, 36299, 195035, 36284,
+ 195036, 36336, 195037, 133342, 195038, 36564, 195039, 36664, 195040, 165330, 195041, 165357,
+ 195042, 37012, 195043, 37105, 195044, 37137, 195045, 165678, 195046, 37147, 195047, 37432,
+ 195048, -37592, 195050, 37500, 195051, 37881, 195052, 37909, 195053, 166906, 195054, 38283,
+ 195055, 18837, 195056, 38327, 195057, 167287, 195058, 18918, 195059, 38595, 195060, 23986,
+ 195061, 38691, 195062, 168261, 195063, 168474, 195064, 19054, 195065, 19062, 195066, 38880,
+ 195067, 168970, 195068, 19122, 195069, 169110, 195070, 38923, 195072, 38953, 195073, 169398,
+ 195074, 39138, 195075, 19251, 195076, 39209, 195077, 39335, 195078, 39362, 195079, 39422,
+ 195080, 19406, 195081, 170800, 195082, 39698, 195083, 40000, 195084, 40189, 195085, 19662,
+ 195086, 19693, 195087, 40295, 195088, 172238, 195089, 19704, 195090, 172293, 195091, 172558,
+ 195092, 172689, 195093, 40635, 195094, 19798, 195095, 40697, 195096, 40702, 195097, 40709,
+ 195098, 40719, 195099, 40726, 195100, 40763, 195101, 173568, 195102, -195103, 917505, 0,
+ 917506, -917507, 917536, 0, 917632, -917633, 917760, 0, 918000, -918001, 2147483647, 0};
UnicodeSimpleCategory get_unicode_simple_category(uint32 code) {
- auto it = std::upper_bound(std::begin(unicode_simple_category_ranges), std::end(unicode_simple_category_ranges),
- (code << 5) + 30);
+ if (code < 128) {
+ return static_cast<UnicodeSimpleCategory>(unicode_simple_category_table[code]);
+ }
+ auto jump_pos_index = code <= 0x20000 ? code >> 7 : (0x20000 >> 7) - (0x20000 >> 16) + (code >> 16);
+ // CHECK(jump_pos_index < sizeof(unicode_simple_category_ranges) / sizeof(unicode_simple_category_ranges[0]));
+ auto it = unicode_simple_category_ranges + unicode_simple_category_jump_pos[jump_pos_index];
+ code = (code << 5) + 30;
+ // CHECK(unicode_simple_category_ranges[unicode_simple_category_jump_pos[jump_pos_index + 1]] > code);
+ while (*it <= code) {
+ ++it;
+ }
return static_cast<UnicodeSimpleCategory>(*(it - 1) & 31);
}
@@ -524,8 +1283,9 @@ static uint32 binary_search_ranges(const int32 (&ranges)[N], uint32 code) {
return 0;
}
- int32 code_int = static_cast<int32>(code);
- size_t l = 0, r = N;
+ auto code_int = static_cast<int32>(code);
+ size_t l = 0;
+ size_t r = N;
while (l < r) {
size_t m = ((l + r + 2) >> 2) << 1;
if (ranges[m] <= code_int) {
@@ -550,7 +1310,7 @@ static uint32 binary_search_ranges(const int32 (&ranges)[N], uint32 code) {
case 2:
return ((code - 1) | 1);
default:
- UNREACHABLE();
+ LOG(FATAL) << code << " " << l << " " << r << " " << t;
return 0;
}
}
@@ -571,4 +1331,12 @@ uint32 unicode_to_lower(uint32 code) {
}
}
+uint32 remove_diacritics(uint32 code) {
+ if (code < TABLE_SIZE) {
+ return without_diacritics_table[code];
+ } else {
+ return binary_search_ranges(without_diacritics_ranges, code);
+ }
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.h
index 1c75397d6e..9012e4633f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/unicode.h
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,7 +16,7 @@ UnicodeSimpleCategory get_unicode_simple_category(uint32 code);
/**
* Prepares unicode character for search, leaving only digits and lowercased letters.
- * Return code of replacing character or 0 if the character should be skipped.
+ * Returns code of replacing character or 0 if the character should be skipped.
*/
uint32 prepare_search_character(uint32 code);
@@ -25,4 +25,9 @@ uint32 prepare_search_character(uint32 code);
*/
uint32 unicode_to_lower(uint32 code);
+/**
+ * Removes diacritics from a unicode character.
+ */
+uint32 remove_diacritics(uint32 code);
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/unique_ptr.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/unique_ptr.h
new file mode 100644
index 0000000000..c8f3b48d1f
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/unique_ptr.h
@@ -0,0 +1,106 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#pragma once
+
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+namespace td {
+
+// const-correct and compiler-friendly (g++ RAM and CPU usage 10 times less than for std::unique_ptr)
+// replacement for std::unique_ptr
+template <class T>
+class unique_ptr final {
+ public:
+ using pointer = T *;
+ using element_type = T;
+
+ unique_ptr() noexcept = default;
+ unique_ptr(const unique_ptr &other) = delete;
+ unique_ptr &operator=(const unique_ptr &other) = delete;
+ unique_ptr(unique_ptr &&other) noexcept : ptr_(other.release()) {
+ }
+ unique_ptr &operator=(unique_ptr &&other) noexcept {
+ reset(other.release());
+ return *this;
+ }
+ ~unique_ptr() {
+ reset();
+ }
+
+ unique_ptr(std::nullptr_t) noexcept {
+ }
+ explicit unique_ptr(T *ptr) noexcept : ptr_(ptr) {
+ }
+ template <class S, class = std::enable_if_t<std::is_base_of<T, S>::value>>
+ unique_ptr(unique_ptr<S> &&other) noexcept : ptr_(static_cast<S *>(other.release())) {
+ }
+ template <class S, class = std::enable_if_t<std::is_base_of<T, S>::value>>
+ unique_ptr &operator=(unique_ptr<S> &&other) noexcept {
+ reset(static_cast<T *>(other.release()));
+ return *this;
+ }
+ void reset(T *new_ptr = nullptr) noexcept {
+ static_assert(sizeof(T) > 0, "Can't destroy unique_ptr with incomplete type");
+ delete ptr_;
+ ptr_ = new_ptr;
+ }
+ T *release() noexcept {
+ auto res = ptr_;
+ ptr_ = nullptr;
+ return res;
+ }
+ T *get() noexcept {
+ return ptr_;
+ }
+ const T *get() const noexcept {
+ return ptr_;
+ }
+ T *operator->() noexcept {
+ return ptr_;
+ }
+ const T *operator->() const noexcept {
+ return ptr_;
+ }
+ T &operator*() noexcept {
+ return *ptr_;
+ }
+ const T &operator*() const noexcept {
+ return *ptr_;
+ }
+ explicit operator bool() const noexcept {
+ return ptr_ != nullptr;
+ }
+
+ private:
+ T *ptr_{nullptr};
+};
+
+template <class T>
+bool operator==(std::nullptr_t, const unique_ptr<T> &p) {
+ return !p;
+}
+template <class T>
+bool operator==(const unique_ptr<T> &p, std::nullptr_t) {
+ return !p;
+}
+template <class T>
+bool operator!=(std::nullptr_t, const unique_ptr<T> &p) {
+ return static_cast<bool>(p);
+}
+template <class T>
+bool operator!=(const unique_ptr<T> &p, std::nullptr_t) {
+ return static_cast<bool>(p);
+}
+
+template <class Type, class... Args>
+unique_ptr<Type> make_unique(Args &&...args) {
+ return unique_ptr<Type>(new Type(std::forward<Args>(args)...));
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.cpp b/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.cpp
index 50f82d6393..16c31e5b2d 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.cpp
@@ -1,12 +1,13 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/utf8.h"
-#include "td/utils/logging.h" // for UNREACHABLE
+#include "td/utils/misc.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/unicode.h"
namespace td {
@@ -15,7 +16,7 @@ bool check_utf8(CSlice str) {
const char *data = str.data();
const char *data_end = data + str.size();
do {
- unsigned int a = static_cast<unsigned char>(*data++);
+ uint32 a = static_cast<unsigned char>(*data++);
if ((a & 0x80) == 0) {
if (data == data_end + 1) {
return true;
@@ -30,25 +31,25 @@ bool check_utf8(CSlice str) {
ENSURE((a & 0x40) != 0);
- unsigned int b = static_cast<unsigned char>(*data++);
+ uint32 b = static_cast<unsigned char>(*data++);
ENSURE((b & 0xc0) == 0x80);
if ((a & 0x20) == 0) {
ENSURE((a & 0x1e) > 0);
continue;
}
- unsigned int c = static_cast<unsigned char>(*data++);
+ uint32 c = static_cast<unsigned char>(*data++);
ENSURE((c & 0xc0) == 0x80);
if ((a & 0x10) == 0) {
- int x = (((a & 0x0f) << 6) | (b & 0x20));
+ uint32 x = (((a & 0x0f) << 6) | (b & 0x20));
ENSURE(x != 0 && x != 0x360); // surrogates
continue;
}
- unsigned int d = static_cast<unsigned char>(*data++);
+ uint32 d = static_cast<unsigned char>(*data++);
ENSURE((d & 0xc0) == 0x80);
if ((a & 0x08) == 0) {
- int t = (((a & 0x07) << 6) | (b & 0x30));
+ uint32 t = (((a & 0x07) << 6) | (b & 0x30));
ENSURE(0 < t && t < 0x110); // end of unicode
continue;
}
@@ -82,30 +83,20 @@ void append_utf8_character(string &str, uint32 ch) {
const unsigned char *next_utf8_unsafe(const unsigned char *ptr, uint32 *code) {
uint32 a = ptr[0];
if ((a & 0x80) == 0) {
- if (code) {
- *code = a;
- }
+ *code = a;
return ptr + 1;
} else if ((a & 0x20) == 0) {
- if (code) {
- *code = ((a & 0x1f) << 6) | (ptr[1] & 0x3f);
- }
+ *code = ((a & 0x1f) << 6) | (ptr[1] & 0x3f);
return ptr + 2;
} else if ((a & 0x10) == 0) {
- if (code) {
- *code = ((a & 0x0f) << 12) | ((ptr[1] & 0x3f) << 6) | (ptr[2] & 0x3f);
- }
+ *code = ((a & 0x0f) << 12) | ((ptr[1] & 0x3f) << 6) | (ptr[2] & 0x3f);
return ptr + 3;
} else if ((a & 0x08) == 0) {
- if (code) {
- *code = ((a & 0x07) << 18) | ((ptr[1] & 0x3f) << 12) | ((ptr[2] & 0x3f) << 6) | (ptr[3] & 0x3f);
- }
+ *code = ((a & 0x07) << 18) | ((ptr[1] & 0x3f) << 12) | ((ptr[2] & 0x3f) << 6) | (ptr[3] & 0x3f);
return ptr + 4;
}
UNREACHABLE();
- if (code) {
- *code = 0;
- }
+ *code = 0;
return ptr;
}
@@ -121,4 +112,84 @@ string utf8_to_lower(Slice str) {
return result;
}
+vector<string> utf8_get_search_words(Slice str) {
+ bool in_word = false;
+ string word;
+ vector<string> words;
+ auto pos = str.ubegin();
+ auto end = str.uend();
+ while (pos != end) {
+ uint32 code;
+ pos = next_utf8_unsafe(pos, &code);
+
+ code = prepare_search_character(code);
+ if (code == 0) {
+ continue;
+ }
+ if (code == ' ') {
+ if (in_word) {
+ words.push_back(std::move(word));
+ word.clear();
+ in_word = false;
+ }
+ } else {
+ in_word = true;
+ code = remove_diacritics(code);
+ append_utf8_character(word, code);
+ }
+ }
+ if (in_word) {
+ words.push_back(std::move(word));
+ }
+ return words;
+}
+
+string utf8_prepare_search_string(Slice str) {
+ return implode(utf8_get_search_words(str));
+}
+
+string utf8_encode(CSlice data) {
+ if (check_utf8(data)) {
+ return data.str();
+ }
+ return PSTRING() << "url_decode(" << url_encode(data) << ')';
+}
+
+size_t utf8_utf16_length(Slice str) {
+ size_t result = 0;
+ for (auto c : str) {
+ result += is_utf8_character_first_code_unit(c) + ((c & 0xf8) == 0xf0);
+ }
+ return result;
+}
+
+Slice utf8_utf16_truncate(Slice str, size_t length) {
+ for (size_t i = 0; i < str.size(); i++) {
+ auto c = static_cast<unsigned char>(str[i]);
+ if (is_utf8_character_first_code_unit(c)) {
+ if (length <= 0) {
+ return str.substr(0, i);
+ } else {
+ length--;
+ if (c >= 0xf0) { // >= 4 bytes in symbol => surrogate pair
+ length--;
+ }
+ }
+ }
+ }
+ return str;
+}
+
+Slice utf8_utf16_substr(Slice str, size_t offset) {
+ if (offset == 0) {
+ return str;
+ }
+ auto offset_pos = utf8_utf16_truncate(str, offset).size();
+ return str.substr(offset_pos);
+}
+
+Slice utf8_utf16_substr(Slice str, size_t offset, size_t length) {
+ return utf8_utf16_truncate(utf8_utf16_substr(str, offset), length);
+}
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.h b/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.h
index 6be1952c19..27c8b5bd5d 100644
--- a/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.h
+++ b/protocols/Telegram/tdlib/td/tdutils/td/utils/utf8.h
@@ -1,12 +1,12 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
-#include "td/utils/int_types.h"
+#include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td {
@@ -28,6 +28,9 @@ inline size_t utf8_length(Slice str) {
return result;
}
+/// returns length of UTF-8 string in UTF-16 code units
+size_t utf8_utf16_length(Slice str);
+
/// appends a Unicode character using UTF-8 encoding
void append_utf8_character(string &str, uint32 ch);
@@ -60,26 +63,13 @@ T utf8_truncate(T str, size_t length) {
}
/// truncates UTF-8 string to the given length given in UTF-16 code units
-template <class T>
-T utf8_utf16_truncate(T str, size_t length) {
- for (size_t i = 0; i < str.size(); i++) {
- auto c = static_cast<unsigned char>(str[i]);
- if (is_utf8_character_first_code_unit(c)) {
- if (length <= 0) {
- return str.substr(0, i);
- } else {
- length--;
- if (c >= 0xf0) { // >= 4 bytes in symbol => surrogaite pair
- length--;
- }
- }
- }
- }
- return str;
-}
+Slice utf8_utf16_truncate(Slice str, size_t length);
template <class T>
T utf8_substr(T str, size_t offset) {
+ if (offset == 0) {
+ return str;
+ }
auto offset_pos = utf8_truncate(str, offset).size();
return str.substr(offset_pos);
}
@@ -89,18 +79,20 @@ T utf8_substr(T str, size_t offset, size_t length) {
return utf8_truncate(utf8_substr(str, offset), length);
}
-template <class T>
-T utf8_utf16_substr(T str, size_t offset) {
- auto offset_pos = utf8_utf16_truncate(str, offset).size();
- return str.substr(offset_pos);
-}
+Slice utf8_utf16_substr(Slice str, size_t offset);
-template <class T>
-T utf8_utf16_substr(T str, size_t offset, size_t length) {
- return utf8_utf16_truncate(utf8_utf16_substr(str, offset), length);
-}
+Slice utf8_utf16_substr(Slice str, size_t offset, size_t length);
/// Returns UTF-8 string converted to lower case.
string utf8_to_lower(Slice str);
+/// Returns UTF-8 string split by words for search.
+vector<string> utf8_get_search_words(Slice str);
+
+/// Returns UTF-8 string prepared for search, leaving only digits and lowercased letters.
+string utf8_prepare_search_string(Slice str);
+
+/// Returns valid UTF-8 representation of the string.
+string utf8_encode(CSlice data);
+
} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/ChainScheduler.cpp b/protocols/Telegram/tdlib/td/tdutils/test/ChainScheduler.cpp
new file mode 100644
index 0000000000..d3bcb934fc
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/ChainScheduler.cpp
@@ -0,0 +1,244 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/algorithm.h"
+#include "td/utils/ChainScheduler.h"
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/Random.h"
+#include "td/utils/Span.h"
+#include "td/utils/StringBuilder.h"
+#include "td/utils/tests.h"
+
+#include <memory>
+#include <numeric>
+
+TEST(ChainScheduler, CreateAfterActive) {
+ td::ChainScheduler<int> scheduler;
+ td::vector<td::ChainScheduler<int>::ChainId> chains{1};
+
+ auto first_task_id = scheduler.create_task(chains, 1);
+ ASSERT_EQ(first_task_id, scheduler.start_next_task().unwrap().task_id);
+ auto second_task_id = scheduler.create_task(chains, 2);
+ ASSERT_EQ(second_task_id, scheduler.start_next_task().unwrap().task_id);
+}
+
+TEST(ChainScheduler, RestartAfterActive) {
+ td::ChainScheduler<int> scheduler;
+ std::vector<td::ChainScheduler<int>::ChainId> chains{1};
+
+ auto first_task_id = scheduler.create_task(chains, 1);
+ auto second_task_id = scheduler.create_task(chains, 2);
+ ASSERT_EQ(first_task_id, scheduler.start_next_task().unwrap().task_id);
+ ASSERT_EQ(second_task_id, scheduler.start_next_task().unwrap().task_id);
+
+ scheduler.reset_task(first_task_id);
+ ASSERT_EQ(first_task_id, scheduler.start_next_task().unwrap().task_id);
+
+ scheduler.reset_task(second_task_id);
+ ASSERT_EQ(second_task_id, scheduler.start_next_task().unwrap().task_id);
+}
+
+TEST(ChainScheduler, SendAfterRestart) {
+ td::ChainScheduler<int> scheduler;
+ std::vector<td::ChainScheduler<int>::ChainId> chains{1};
+
+ auto first_task_id = scheduler.create_task(chains, 1);
+ auto second_task_id = scheduler.create_task(chains, 2);
+ ASSERT_EQ(first_task_id, scheduler.start_next_task().unwrap().task_id);
+ ASSERT_EQ(second_task_id, scheduler.start_next_task().unwrap().task_id);
+
+ scheduler.reset_task(first_task_id);
+
+ scheduler.create_task(chains, 3);
+
+ ASSERT_EQ(first_task_id, scheduler.start_next_task().unwrap().task_id);
+ ASSERT_TRUE(!scheduler.start_next_task());
+}
+
+TEST(ChainScheduler, Basic) {
+ td::ChainScheduler<int> scheduler;
+ for (int i = 0; i < 100; i++) {
+ scheduler.create_task({td::ChainScheduler<int>::ChainId{1}}, i);
+ }
+ int j = 0;
+ while (j != 100) {
+ td::vector<td::ChainScheduler<int>::TaskId> tasks;
+ while (true) {
+ auto o_task_id = scheduler.start_next_task();
+ if (!o_task_id) {
+ break;
+ }
+ auto task_id = o_task_id.value().task_id;
+ auto extra = *scheduler.get_task_extra(task_id);
+ auto parents =
+ td::transform(o_task_id.value().parents, [&](auto parent) { return *scheduler.get_task_extra(parent); });
+ LOG(INFO) << "Start " << extra << parents;
+ CHECK(extra == j);
+ j++;
+ tasks.push_back(task_id);
+ }
+ for (auto &task_id : tasks) {
+ auto extra = *scheduler.get_task_extra(task_id);
+ LOG(INFO) << "Finish " << extra;
+ scheduler.finish_task(task_id);
+ }
+ }
+}
+
+struct ChainSchedulerQuery;
+using QueryPtr = std::shared_ptr<ChainSchedulerQuery>;
+using ChainId = td::ChainScheduler<QueryPtr>::ChainId;
+using TaskId = td::ChainScheduler<QueryPtr>::TaskId;
+
+struct ChainSchedulerQuery {
+ int id{};
+ TaskId task_id{};
+ bool is_ok{};
+ bool skipped{};
+};
+
+TEST(ChainScheduler, Stress) {
+ td::Random::Xorshift128plus rnd(123);
+ int max_query_id = 100000;
+ int MAX_INFLIGHT_QUERIES = 20;
+ int ChainsN = 4;
+
+ struct QueryWithParents {
+ TaskId task_id;
+ QueryPtr id;
+ td::vector<QueryPtr> parents;
+ };
+ td::vector<QueryWithParents> active_queries;
+
+ td::ChainScheduler<QueryPtr> scheduler;
+ td::vector<td::vector<QueryPtr>> chains(ChainsN);
+ int inflight_queries{};
+ int current_query_id{};
+ int sent_cnt{};
+ bool done = false;
+ std::vector<TaskId> pending_queries;
+
+ auto schedule_new_query = [&] {
+ if (current_query_id > max_query_id) {
+ if (inflight_queries == 0) {
+ done = true;
+ }
+ return;
+ }
+ if (inflight_queries >= MAX_INFLIGHT_QUERIES) {
+ return;
+ }
+ auto query_id = current_query_id++;
+ auto query = std::make_shared<ChainSchedulerQuery>();
+ query->id = query_id;
+ int chain_n = rnd.fast(1, ChainsN);
+ td::vector<ChainId> chain_ids(ChainsN);
+ std::iota(chain_ids.begin(), chain_ids.end(), 0);
+ td::random_shuffle(td::as_mutable_span(chain_ids), rnd);
+ chain_ids.resize(chain_n);
+ for (auto chain_id : chain_ids) {
+ chains[td::narrow_cast<size_t>(chain_id)].push_back(query);
+ }
+ auto task_id = scheduler.create_task(chain_ids, query);
+ query->task_id = task_id;
+ pending_queries.push_back(task_id);
+ inflight_queries++;
+ };
+
+ auto check_parents_ok = [&](const QueryWithParents &query_with_parents) -> bool {
+ return td::all_of(query_with_parents.parents, [](auto &parent) { return parent->is_ok; });
+ };
+
+ auto to_query_ptr = [&](TaskId task_id) {
+ return *scheduler.get_task_extra(task_id);
+ };
+ auto flush_pending_queries = [&] {
+ while (true) {
+ auto o_task_with_parents = scheduler.start_next_task();
+ if (!o_task_with_parents) {
+ break;
+ }
+ auto task_with_parents = o_task_with_parents.unwrap();
+ QueryWithParents query_with_parents;
+ query_with_parents.task_id = task_with_parents.task_id;
+ query_with_parents.id = to_query_ptr(task_with_parents.task_id);
+ query_with_parents.parents = td::transform(task_with_parents.parents, to_query_ptr);
+ active_queries.push_back(query_with_parents);
+ sent_cnt++;
+ }
+ };
+ auto skip_one_query = [&] {
+ if (pending_queries.empty()) {
+ return;
+ }
+ auto it = pending_queries.begin() + rnd.fast(0, static_cast<int>(pending_queries.size()) - 1);
+ auto task_id = *it;
+ pending_queries.erase(it);
+ td::remove_if(active_queries, [&](auto &q) { return q.task_id == task_id; });
+
+ auto query = *scheduler.get_task_extra(task_id);
+ query->skipped = true;
+ scheduler.finish_task(task_id);
+ inflight_queries--;
+ LOG(INFO) << "Skip " << query->id;
+ };
+ auto execute_one_query = [&] {
+ if (active_queries.empty()) {
+ return;
+ }
+ auto it = active_queries.begin() + rnd.fast(0, static_cast<int>(active_queries.size()) - 1);
+ auto query_with_parents = *it;
+ active_queries.erase(it);
+
+ auto query = query_with_parents.id;
+ if (rnd.fast(0, 20) == 0) {
+ scheduler.finish_task(query->task_id);
+ td::remove(pending_queries, query->task_id);
+ inflight_queries--;
+ LOG(INFO) << "Fail " << query->id;
+ } else if (check_parents_ok(query_with_parents)) {
+ query->is_ok = true;
+ scheduler.finish_task(query->task_id);
+ td::remove(pending_queries, query->task_id);
+ inflight_queries--;
+ LOG(INFO) << "OK " << query->id;
+ } else {
+ scheduler.reset_task(query->task_id);
+ LOG(INFO) << "Reset " << query->id;
+ }
+ };
+
+ td::RandomSteps steps({{schedule_new_query, 100}, {execute_one_query, 100}, {skip_one_query, 10}});
+ while (!done) {
+ steps.step(rnd);
+ flush_pending_queries();
+ // LOG(INFO) << scheduler;
+ }
+ LOG(INFO) << "Sent queries count " << sent_cnt;
+ LOG(INFO) << "Total queries " << current_query_id;
+ for (auto &chain : chains) {
+ int prev_ok = -1;
+ int failed_cnt = 0;
+ int ok_cnt = 0;
+ int skipped_cnt = 0;
+ for (auto &q : chain) {
+ if (q->is_ok) {
+ CHECK(prev_ok < q->id);
+ prev_ok = q->id;
+ ok_cnt++;
+ } else {
+ if (q->skipped) {
+ skipped_cnt++;
+ } else {
+ failed_cnt++;
+ }
+ }
+ }
+ LOG(INFO) << "Chain ok " << ok_cnt << " failed " << failed_cnt << " skipped " << skipped_cnt;
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/ConcurrentHashMap.cpp b/protocols/Telegram/tdlib/td/tdutils/test/ConcurrentHashMap.cpp
new file mode 100644
index 0000000000..a90f11d525
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/ConcurrentHashMap.cpp
@@ -0,0 +1,252 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/benchmark.h"
+#include "td/utils/common.h"
+#include "td/utils/ConcurrentHashTable.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/Mutex.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/SpinLock.h"
+#include "td/utils/tests.h"
+
+#include <atomic>
+
+#if !TD_THREAD_UNSUPPORTED
+
+#if TD_HAVE_ABSL
+#include <absl/container/flat_hash_map.h>
+#else
+#include <unordered_map>
+#endif
+
+#if TD_WITH_LIBCUCKOO
+#include <third-party/libcuckoo/libcuckoo/cuckoohash_map.hh>
+#endif
+
+#if TD_WITH_JUNCTION
+#include <junction/ConcurrentMap_Grampa.h>
+#include <junction/ConcurrentMap_Leapfrog.h>
+#include <junction/ConcurrentMap_Linear.h>
+#endif
+
+// Non resizable HashMap. Just an example
+template <class KeyT, class ValueT>
+class ArrayHashMap {
+ public:
+ explicit ArrayHashMap(std::size_t n) : array_(n) {
+ }
+ struct Node {
+ std::atomic<KeyT> key{KeyT{}};
+ std::atomic<ValueT> value{ValueT{}};
+ };
+ static td::string get_name() {
+ return "ArrayHashMap";
+ }
+ KeyT empty_key() const {
+ return KeyT{};
+ }
+
+ void insert(KeyT key, ValueT value) {
+ array_.with_value(key, true, [&](auto &node_value) { node_value.store(value, std::memory_order_release); });
+ }
+ ValueT find(KeyT key, ValueT value) {
+ array_.with_value(key, false, [&](auto &node_value) { value = node_value.load(std::memory_order_acquire); });
+ return value;
+ }
+
+ private:
+ td::AtomicHashArray<KeyT, std::atomic<ValueT>> array_;
+};
+
+template <class KeyT, class ValueT>
+class ConcurrentHashMapMutex {
+ public:
+ explicit ConcurrentHashMapMutex(std::size_t) {
+ }
+ static td::string get_name() {
+ return "ConcurrentHashMapMutex";
+ }
+ void insert(KeyT key, ValueT value) {
+ auto guard = mutex_.lock();
+ hash_map_.emplace(key, value);
+ }
+ ValueT find(KeyT key, ValueT default_value) {
+ auto guard = mutex_.lock();
+ auto it = hash_map_.find(key);
+ if (it == hash_map_.end()) {
+ return default_value;
+ }
+ return it->second;
+ }
+
+ private:
+ td::Mutex mutex_;
+#if TD_HAVE_ABSL
+ absl::flat_hash_map<KeyT, ValueT> hash_map_;
+#else
+ std::unordered_map<KeyT, ValueT, td::Hash<KeyT>> hash_map_;
+#endif
+};
+
+template <class KeyT, class ValueT>
+class ConcurrentHashMapSpinlock {
+ public:
+ explicit ConcurrentHashMapSpinlock(size_t) {
+ }
+ static td::string get_name() {
+ return "ConcurrentHashMapSpinlock";
+ }
+ void insert(KeyT key, ValueT value) {
+ auto guard = spinlock_.lock();
+ hash_map_.emplace(key, value);
+ }
+ ValueT find(KeyT key, ValueT default_value) {
+ auto guard = spinlock_.lock();
+ auto it = hash_map_.find(key);
+ if (it == hash_map_.end()) {
+ return default_value;
+ }
+ return it->second;
+ }
+
+ private:
+ td::SpinLock spinlock_;
+#if TD_HAVE_ABSL
+ absl::flat_hash_map<KeyT, ValueT> hash_map_;
+#else
+ std::unordered_map<KeyT, ValueT, td::Hash<KeyT>> hash_map_;
+#endif
+};
+
+#if TD_WITH_LIBCUCKOO
+template <class KeyT, class ValueT>
+class ConcurrentHashMapLibcuckoo {
+ public:
+ explicit ConcurrentHashMapLibcuckoo(size_t) {
+ }
+ static td::string get_name() {
+ return "ConcurrentHashMapLibcuckoo";
+ }
+ void insert(KeyT key, ValueT value) {
+ hash_map_.insert(key, value);
+ }
+ ValueT find(KeyT key, ValueT default_value) {
+ hash_map_.find(key, default_value);
+ return default_value;
+ }
+
+ private:
+ cuckoohash_map<KeyT, ValueT> hash_map_;
+};
+#endif
+
+#if TD_WITH_JUNCTION
+template <class KeyT, class ValueT>
+class ConcurrentHashMapJunction {
+ public:
+ explicit ConcurrentHashMapJunction(std::size_t size) : hash_map_() {
+ }
+ static td::string get_name() {
+ return "ConcurrentHashMapJunction";
+ }
+ void insert(KeyT key, ValueT value) {
+ hash_map_.assign(key, value);
+ }
+ ValueT find(KeyT key, ValueT default_value) {
+ return hash_map_.get(key);
+ }
+
+ ConcurrentHashMapJunction(const ConcurrentHashMapJunction &) = delete;
+ ConcurrentHashMapJunction &operator=(const ConcurrentHashMapJunction &) = delete;
+ ConcurrentHashMapJunction(ConcurrentHashMapJunction &&other) = delete;
+ ConcurrentHashMapJunction &operator=(ConcurrentHashMapJunction &&) = delete;
+ ~ConcurrentHashMapJunction() {
+ junction::DefaultQSBR.flush();
+ }
+
+ private:
+ junction::ConcurrentMap_Leapfrog<KeyT, ValueT> hash_map_;
+};
+#endif
+
+template <class HashMap>
+class HashMapBenchmark final : public td::Benchmark {
+ struct Query {
+ int key;
+ int value;
+ };
+ td::vector<Query> queries;
+ td::unique_ptr<HashMap> hash_map;
+
+ std::size_t threads_n = 16;
+ static constexpr std::size_t MUL = 7273; //1000000000 + 7;
+ int n_ = 0;
+
+ public:
+ explicit HashMapBenchmark(std::size_t threads_n) : threads_n(threads_n) {
+ }
+ td::string get_description() const final {
+ return HashMap::get_name();
+ }
+ void start_up_n(int n) final {
+ n *= static_cast<int>(threads_n);
+ n_ = n;
+ hash_map = td::make_unique<HashMap>(n * 2);
+ }
+
+ void run(int n) final {
+ n = n_;
+ td::vector<td::thread> threads;
+
+ for (std::size_t i = 0; i < threads_n; i++) {
+ std::size_t l = n * i / threads_n;
+ std::size_t r = n * (i + 1) / threads_n;
+ threads.emplace_back([l, r, this] {
+ for (size_t i = l; i < r; i++) {
+ auto x = td::narrow_cast<int>((i + 1) * MUL % n_) + 3;
+ auto y = td::narrow_cast<int>(i + 2);
+ hash_map->insert(x, y);
+ }
+ });
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ }
+
+ void tear_down() final {
+ for (int i = 0; i < n_; i++) {
+ auto x = td::narrow_cast<int>((i + 1) * MUL % n_) + 3;
+ auto y = td::narrow_cast<int>(i + 2);
+ ASSERT_EQ(y, hash_map->find(x, -1));
+ }
+ queries.clear();
+ hash_map.reset();
+ }
+};
+
+template <class HashMap>
+static void bench_hash_map() {
+ td::bench(HashMapBenchmark<HashMap>(16));
+ td::bench(HashMapBenchmark<HashMap>(1));
+}
+
+TEST(ConcurrentHashMap, Benchmark) {
+ bench_hash_map<td::ConcurrentHashMap<td::int32, td::int32>>();
+ bench_hash_map<ArrayHashMap<td::int32, td::int32>>();
+ bench_hash_map<ConcurrentHashMapSpinlock<td::int32, td::int32>>();
+ bench_hash_map<ConcurrentHashMapMutex<td::int32, td::int32>>();
+#if TD_WITH_LIBCUCKOO
+ bench_hash_map<ConcurrentHashMapLibcuckoo<td::int32, td::int32>>();
+#endif
+#if TD_WITH_JUNCTION
+ bench_hash_map<ConcurrentHashMapJunction<td::int32, td::int32>>();
+#endif
+}
+
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp b/protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp
index b617485462..210ab415cc 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/Enumerator.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/EpochBasedMemoryReclamation.cpp b/protocols/Telegram/tdlib/td/tdutils/test/EpochBasedMemoryReclamation.cpp
new file mode 100644
index 0000000000..c97679bb83
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/EpochBasedMemoryReclamation.cpp
@@ -0,0 +1,68 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/EpochBasedMemoryReclamation.h"
+#include "td/utils/logging.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+
+#include <atomic>
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(EpochBaseMemoryReclamation, stress) {
+ struct Node {
+ std::atomic<std::string *> name_{nullptr};
+ char pad[64];
+ };
+
+ int threads_n = 10;
+ std::vector<Node> nodes(threads_n);
+ td::EpochBasedMemoryReclamation<std::string> ebmr(threads_n + 1);
+ auto locker = ebmr.get_locker(threads_n);
+ locker.lock();
+ locker.unlock();
+ std::vector<td::thread> threads(threads_n);
+ int thread_id = 0;
+ for (auto &thread : threads) {
+ thread = td::thread([&, thread_id] {
+ auto locker = ebmr.get_locker(thread_id);
+ locker.lock();
+ for (int i = 0; i < 1000000; i++) {
+ auto &node = nodes[td::Random::fast(0, threads_n - 1)];
+ auto *str = node.name_.load(std::memory_order_acquire);
+ if (str) {
+ CHECK(*str == "one" || *str == "twotwo");
+ }
+ if ((i + 1) % 100 == 0) {
+ locker.retire();
+ }
+ if (td::Random::fast(0, 5) == 0) {
+ auto *new_str = new td::string(td::Random::fast_bool() ? "one" : "twotwo");
+ if (node.name_.compare_exchange_strong(str, new_str, std::memory_order_acq_rel)) {
+ locker.retire(str);
+ } else {
+ delete new_str;
+ }
+ }
+ }
+ locker.retire_sync();
+ locker.unlock();
+ });
+ thread_id++;
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ LOG(INFO) << "Undeleted pointers: " << ebmr.to_delete_size_unsafe();
+ //CHECK(static_cast<int>(ebmr.to_delete_size_unsafe()) <= threads_n * threads_n);
+ for (int i = 0; i < threads_n; i++) {
+ ebmr.get_locker(i).retire_sync();
+ }
+ CHECK(ebmr.to_delete_size_unsafe() == 0);
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/HashSet.cpp b/protocols/Telegram/tdlib/td/tdutils/test/HashSet.cpp
new file mode 100644
index 0000000000..94ebf8733b
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/HashSet.cpp
@@ -0,0 +1,438 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/algorithm.h"
+#include "td/utils/common.h"
+#include "td/utils/FlatHashMap.h"
+#include "td/utils/FlatHashMapChunks.h"
+#include "td/utils/FlatHashSet.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/logging.h"
+#include "td/utils/Random.h"
+#include "td/utils/Slice.h"
+#include "td/utils/tests.h"
+
+#include <algorithm>
+#include <array>
+#include <random>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+template <class T>
+static auto extract_kv(const T &reference) {
+ auto expected = td::transform(reference, [](auto &it) { return std::make_pair(it.first, it.second); });
+ std::sort(expected.begin(), expected.end());
+ return expected;
+}
+
+template <class T>
+static auto extract_k(const T &reference) {
+ auto expected = td::transform(reference, [](auto &it) { return it; });
+ std::sort(expected.begin(), expected.end());
+ return expected;
+}
+
+TEST(FlatHashMapChunks, basic) {
+ td::FlatHashMapChunks<int, int> kv;
+ kv[5] = 3;
+ ASSERT_EQ(3, kv[5]);
+ kv[3] = 4;
+ ASSERT_EQ(4, kv[3]);
+}
+
+TEST(FlatHashMap, probing) {
+ auto test = [](int buckets, int elements) {
+ CHECK(buckets >= elements);
+ td::vector<bool> data(buckets, false);
+ std::random_device rnd;
+ std::mt19937 mt(rnd());
+ std::uniform_int_distribution<td::int32> d(0, buckets - 1);
+ for (int i = 0; i < elements; i++) {
+ int pos = d(mt);
+ while (data[pos]) {
+ pos++;
+ if (pos == buckets) {
+ pos = 0;
+ }
+ }
+ data[pos] = true;
+ }
+ int max_chain = 0;
+ int cur_chain = 0;
+ for (auto x : data) {
+ if (x) {
+ cur_chain++;
+ max_chain = td::max(max_chain, cur_chain);
+ } else {
+ cur_chain = 0;
+ }
+ }
+ LOG(INFO) << "Buckets=" << buckets << " elements=" << elements << " max_chain=" << max_chain;
+ };
+ test(8192, static_cast<int>(8192 * 0.8));
+ test(8192, static_cast<int>(8192 * 0.6));
+ test(8192, static_cast<int>(8192 * 0.3));
+}
+
+struct A {
+ int a;
+};
+
+struct AHash {
+ td::uint32 operator()(A a) const {
+ return td::Hash<int>()(a.a);
+ }
+};
+
+static bool operator==(const A &lhs, const A &rhs) {
+ return lhs.a == rhs.a;
+}
+
+TEST(FlatHashSet, foreach) {
+ td::FlatHashSet<A, AHash> s;
+ for (auto it : s) {
+ LOG(ERROR) << it.a;
+ }
+ s.insert({1});
+ LOG(INFO) << s.begin()->a;
+}
+
+TEST(FlatHashSet, TL) {
+ td::FlatHashSet<int> s;
+ int N = 100000;
+ for (int i = 0; i < 10000000; i++) {
+ s.insert((i + N / 2) % N + 1);
+ s.erase(i % N + 1);
+ }
+}
+
+TEST(FlatHashMap, basic) {
+ {
+ td::FlatHashMap<td::int32, int> map;
+ map[1] = 2;
+ ASSERT_EQ(2, map[1]);
+ ASSERT_EQ(1, map.find(1)->first);
+ ASSERT_EQ(2, map.find(1)->second);
+ // ASSERT_EQ(1, map.find(1)->key());
+ // ASSERT_EQ(2, map.find(1)->value());
+ for (auto &kv : map) {
+ ASSERT_EQ(1, kv.first);
+ ASSERT_EQ(2, kv.second);
+ }
+ map.erase(map.find(1));
+ }
+
+ td::FlatHashMap<td::int32, std::array<td::unique_ptr<td::string>, 10>> x;
+ auto y = std::move(x);
+ x[12];
+ x.erase(x.find(12));
+
+ {
+ td::FlatHashMap<td::int32, td::string> map = {{1, "hello"}, {2, "world"}};
+ ASSERT_EQ("hello", map[1]);
+ ASSERT_EQ("world", map[2]);
+ ASSERT_EQ(2u, map.size());
+ ASSERT_EQ("", map[3]);
+ ASSERT_EQ(3u, map.size());
+ }
+
+ {
+ td::FlatHashMap<td::int32, td::string> map = {{1, "hello"}, {1, "world"}};
+ ASSERT_EQ("hello", map[1]);
+ ASSERT_EQ(1u, map.size());
+ }
+
+ using KV = td::FlatHashMap<td::string, td::string>;
+ using Data = td::vector<std::pair<td::string, td::string>>;
+ auto data = Data{{"a", "b"}, {"c", "d"}};
+ { ASSERT_EQ(Data{}, extract_kv(KV())); }
+
+ {
+ KV kv;
+ for (auto &pair : data) {
+ kv.emplace(pair.first, pair.second);
+ }
+ ASSERT_EQ(data, extract_kv(kv));
+
+ KV moved_kv(std::move(kv));
+ ASSERT_EQ(data, extract_kv(moved_kv));
+ ASSERT_EQ(Data{}, extract_kv(kv));
+ ASSERT_TRUE(kv.empty());
+ kv = std::move(moved_kv);
+ ASSERT_EQ(data, extract_kv(kv));
+
+ KV assign_moved_kv;
+ assign_moved_kv = std::move(kv);
+ ASSERT_EQ(data, extract_kv(assign_moved_kv));
+ ASSERT_EQ(Data{}, extract_kv(kv));
+ ASSERT_TRUE(kv.empty());
+ kv = std::move(assign_moved_kv);
+
+ KV it_copy_kv;
+ for (auto &pair : kv) {
+ it_copy_kv.emplace(pair.first, pair.second);
+ }
+ ASSERT_EQ(data, extract_kv(it_copy_kv));
+ }
+
+ {
+ KV kv;
+ ASSERT_TRUE(kv.empty());
+ ASSERT_EQ(0u, kv.size());
+ for (auto &pair : data) {
+ kv.emplace(pair.first, pair.second);
+ }
+ ASSERT_TRUE(!kv.empty());
+ ASSERT_EQ(2u, kv.size());
+
+ ASSERT_EQ("a", kv.find("a")->first);
+ ASSERT_EQ("b", kv.find("a")->second);
+ kv.find("a")->second = "c";
+ ASSERT_EQ("c", kv.find("a")->second);
+ ASSERT_EQ("c", kv["a"]);
+
+ ASSERT_EQ(0u, kv.count("x"));
+ ASSERT_EQ(1u, kv.count("a"));
+ }
+ {
+ KV kv;
+ kv["d"];
+ ASSERT_EQ((Data{{"d", ""}}), extract_kv(kv));
+ kv.erase(kv.find("d"));
+ ASSERT_EQ(Data{}, extract_kv(kv));
+ }
+}
+
+TEST(FlatHashMap, remove_if_basic) {
+ td::Random::Xorshift128plus rnd(123);
+
+ constexpr int TESTS_N = 1000;
+ constexpr int MAX_TABLE_SIZE = 1000;
+ for (int test_i = 0; test_i < TESTS_N; test_i++) {
+ std::unordered_map<td::uint64, td::uint64, td::Hash<td::uint64>> reference;
+ td::FlatHashMap<td::uint64, td::uint64> table;
+ int N = rnd.fast(1, MAX_TABLE_SIZE);
+ for (int i = 0; i < N; i++) {
+ auto key = rnd();
+ auto value = i;
+ reference[key] = value;
+ table[key] = value;
+ }
+ ASSERT_EQ(extract_kv(reference), extract_kv(table));
+
+ td::vector<std::pair<td::uint64, td::uint64>> kv;
+ td::table_remove_if(table, [&](auto &it) {
+ kv.emplace_back(it.first, it.second);
+ return it.second % 2 == 0;
+ });
+ std::sort(kv.begin(), kv.end());
+ ASSERT_EQ(extract_kv(reference), kv);
+
+ td::table_remove_if(reference, [](auto &it) { return it.second % 2 == 0; });
+ ASSERT_EQ(extract_kv(reference), extract_kv(table));
+ }
+}
+
+static constexpr size_t MAX_TABLE_SIZE = 1000;
+TEST(FlatHashMap, stress_test) {
+ td::Random::Xorshift128plus rnd(123);
+ size_t max_table_size = MAX_TABLE_SIZE; // dynamic value
+ std::unordered_map<td::uint64, td::uint64, td::Hash<td::uint64>> ref;
+ td::FlatHashMap<td::uint64, td::uint64> tbl;
+
+ auto validate = [&] {
+ ASSERT_EQ(ref.empty(), tbl.empty());
+ ASSERT_EQ(ref.size(), tbl.size());
+ ASSERT_EQ(extract_kv(ref), extract_kv(tbl));
+ for (auto &kv : ref) {
+ auto tbl_it = tbl.find(kv.first);
+ ASSERT_TRUE(tbl_it != tbl.end());
+ ASSERT_EQ(kv.second, tbl_it->second);
+ }
+ };
+
+ td::vector<td::RandomSteps::Step> steps;
+ auto add_step = [&](td::Slice step_name, td::uint32 weight, auto f) {
+ auto g = [&, f = std::move(f)] {
+ //ASSERT_EQ(ref.size(), tbl.size());
+ f();
+ ASSERT_EQ(ref.size(), tbl.size());
+ //validate();
+ };
+ steps.emplace_back(td::RandomSteps::Step{std::move(g), weight});
+ };
+
+ auto gen_key = [&] {
+ auto key = rnd() % 4000 + 1;
+ return key;
+ };
+
+ add_step("Reset hash table", 1, [&] {
+ validate();
+ td::reset_to_empty(ref);
+ td::reset_to_empty(tbl);
+ max_table_size = rnd.fast(1, MAX_TABLE_SIZE);
+ });
+ add_step("Clear hash table", 1, [&] {
+ validate();
+ ref.clear();
+ tbl.clear();
+ max_table_size = rnd.fast(1, MAX_TABLE_SIZE);
+ });
+
+ add_step("Insert random value", 1000, [&] {
+ if (tbl.size() > max_table_size) {
+ return;
+ }
+ auto key = gen_key();
+ auto value = rnd();
+ ref[key] = value;
+ tbl[key] = value;
+ ASSERT_EQ(ref[key], tbl[key]);
+ });
+
+ add_step("Emplace random value", 1000, [&] {
+ if (tbl.size() > max_table_size) {
+ return;
+ }
+ auto key = gen_key();
+ auto value = rnd();
+ auto ref_it = ref.emplace(key, value);
+ auto tbl_it = tbl.emplace(key, value);
+ ASSERT_EQ(ref_it.second, tbl_it.second);
+ ASSERT_EQ(key, tbl_it.first->first);
+ });
+
+ add_step("empty operator[]", 1000, [&] {
+ if (tbl.size() > max_table_size) {
+ return;
+ }
+ auto key = gen_key();
+ ASSERT_EQ(ref[key], tbl[key]);
+ });
+
+ add_step("reserve", 10, [&] { tbl.reserve(static_cast<size_t>(rnd() % max_table_size)); });
+
+ add_step("find", 1000, [&] {
+ auto key = gen_key();
+ auto ref_it = ref.find(key);
+ auto tbl_it = tbl.find(key);
+ ASSERT_EQ(ref_it == ref.end(), tbl_it == tbl.end());
+ if (ref_it != ref.end()) {
+ ASSERT_EQ(ref_it->first, tbl_it->first);
+ ASSERT_EQ(ref_it->second, tbl_it->second);
+ }
+ });
+
+ add_step("find_and_erase", 100, [&] {
+ auto key = gen_key();
+ auto ref_it = ref.find(key);
+ auto tbl_it = tbl.find(key);
+ ASSERT_EQ(ref_it == ref.end(), tbl_it == tbl.end());
+ if (ref_it != ref.end()) {
+ ref.erase(ref_it);
+ tbl.erase(tbl_it);
+ }
+ });
+
+ add_step("remove_if", 5, [&] {
+ auto mul = rnd();
+ auto bit = rnd() % 64;
+ auto condition = [&](auto &it) {
+ return (((it.second * mul) >> bit) & 1) == 0;
+ };
+ td::table_remove_if(tbl, condition);
+ td::table_remove_if(ref, condition);
+ });
+
+ td::RandomSteps runner(std::move(steps));
+ for (size_t i = 0; i < 1000000; i++) {
+ runner.step(rnd);
+ }
+}
+
+TEST(FlatHashSet, stress_test) {
+ td::vector<td::RandomSteps::Step> steps;
+ auto add_step = [&steps](td::Slice, td::uint32 weight, auto f) {
+ steps.emplace_back(td::RandomSteps::Step{std::move(f), weight});
+ };
+
+ td::Random::Xorshift128plus rnd(123);
+ size_t max_table_size = MAX_TABLE_SIZE; // dynamic value
+ std::unordered_set<td::uint64, td::Hash<td::uint64>> ref;
+ td::FlatHashSet<td::uint64> tbl;
+
+ auto validate = [&] {
+ ASSERT_EQ(ref.empty(), tbl.empty());
+ ASSERT_EQ(ref.size(), tbl.size());
+ ASSERT_EQ(extract_k(ref), extract_k(tbl));
+ };
+ auto gen_key = [&] {
+ auto key = rnd() % 4000 + 1;
+ return key;
+ };
+
+ add_step("Reset hash table", 1, [&] {
+ validate();
+ td::reset_to_empty(ref);
+ td::reset_to_empty(tbl);
+ max_table_size = rnd.fast(1, MAX_TABLE_SIZE);
+ });
+ add_step("Clear hash table", 1, [&] {
+ validate();
+ ref.clear();
+ tbl.clear();
+ max_table_size = rnd.fast(1, MAX_TABLE_SIZE);
+ });
+
+ add_step("Insert random value", 1000, [&] {
+ if (tbl.size() > max_table_size) {
+ return;
+ }
+ auto key = gen_key();
+ ref.insert(key);
+ tbl.insert(key);
+ });
+
+ add_step("reserve", 10, [&] { tbl.reserve(static_cast<size_t>(rnd() % max_table_size)); });
+
+ add_step("find", 1000, [&] {
+ auto key = gen_key();
+ auto ref_it = ref.find(key);
+ auto tbl_it = tbl.find(key);
+ ASSERT_EQ(ref_it == ref.end(), tbl_it == tbl.end());
+ if (ref_it != ref.end()) {
+ ASSERT_EQ(*ref_it, *tbl_it);
+ }
+ });
+
+ add_step("find_and_erase", 100, [&] {
+ auto key = gen_key();
+ auto ref_it = ref.find(key);
+ auto tbl_it = tbl.find(key);
+ ASSERT_EQ(ref_it == ref.end(), tbl_it == tbl.end());
+ if (ref_it != ref.end()) {
+ ref.erase(ref_it);
+ tbl.erase(tbl_it);
+ }
+ });
+
+ add_step("remove_if", 5, [&] {
+ auto mul = rnd();
+ auto bit = rnd() % 64;
+ auto condition = [&](auto &it) {
+ return (((it * mul) >> bit) & 1) == 0;
+ };
+ td::table_remove_if(tbl, condition);
+ td::table_remove_if(ref, condition);
+ });
+
+ td::RandomSteps runner(std::move(steps));
+ for (size_t i = 0; i < 10000000; i++) {
+ runner.step(rnd);
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp b/protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp
index 36b0570530..0c4174db0f 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/HazardPointers.cpp
@@ -1,13 +1,15 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
+#include "td/utils/common.h"
#include "td/utils/HazardPointers.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread.h"
#include "td/utils/Random.h"
+#include "td/utils/Slice.h"
#include "td/utils/tests.h"
#include <atomic>
@@ -15,7 +17,7 @@
#if !TD_THREAD_UNSUPPORTED
TEST(HazardPointers, stress) {
struct Node {
- std::atomic<std::string *> name_;
+ std::atomic<std::string *> name_{nullptr};
char pad[64];
};
int threads_n = 10;
@@ -25,16 +27,16 @@ TEST(HazardPointers, stress) {
int thread_id = 0;
for (auto &thread : threads) {
thread = td::thread([&, thread_id] {
- auto holder = hazard_pointers.get_holder(thread_id, 0);
+ std::remove_reference_t<decltype(hazard_pointers)>::Holder holder(hazard_pointers, thread_id, 0);
for (int i = 0; i < 1000000; i++) {
auto &node = nodes[td::Random::fast(0, threads_n - 1)];
auto *str = holder.protect(node.name_);
if (str) {
- CHECK(*str == "one" || *str == "twotwo");
+ CHECK(*str == td::Slice("one") || *str == td::Slice("twotwo"));
}
holder.clear();
if (td::Random::fast(0, 5) == 0) {
- std::string *new_str = new std::string(td::Random::fast(0, 1) == 0 ? "one" : "twotwo");
+ auto *new_str = new td::string(td::Random::fast_bool() ? "one" : "twotwo");
if (node.name_.compare_exchange_strong(str, new_str, std::memory_order_acq_rel)) {
hazard_pointers.retire(thread_id, str);
} else {
@@ -48,11 +50,11 @@ TEST(HazardPointers, stress) {
for (auto &thread : threads) {
thread.join();
}
- LOG(ERROR) << "Undeleted pointers: " << hazard_pointers.to_delete_size_unsafe();
+ LOG(INFO) << "Undeleted pointers: " << hazard_pointers.to_delete_size_unsafe();
CHECK(static_cast<int>(hazard_pointers.to_delete_size_unsafe()) <= threads_n * threads_n);
for (int i = 0; i < threads_n; i++) {
hazard_pointers.retire(i);
}
CHECK(hazard_pointers.to_delete_size_unsafe() == 0);
}
-#endif //!TD_THREAD_UNSUPPORTED
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/HttpUrl.cpp b/protocols/Telegram/tdlib/td/tdutils/test/HttpUrl.cpp
new file mode 100644
index 0000000000..6e91d48803
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/HttpUrl.cpp
@@ -0,0 +1,54 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/HttpUrl.h"
+#include "td/utils/tests.h"
+
+#include <utility>
+
+static void test_get_url_query_file_name(const char *prefix, const char *suffix, const char *file_name) {
+ auto path = td::string(prefix) + td::string(file_name) + td::string(suffix);
+ ASSERT_STREQ(file_name, td::get_url_query_file_name(path));
+ ASSERT_STREQ(file_name, td::get_url_file_name("http://telegram.org" + path));
+ ASSERT_STREQ(file_name, td::get_url_file_name("http://telegram.org:80" + path));
+ ASSERT_STREQ(file_name, td::get_url_file_name("telegram.org" + path));
+}
+
+TEST(HttpUrl, get_url_query_file_name) {
+ for (auto suffix : {"?t=1#test", "#test?t=1", "#?t=1", "?t=1#", "#test", "?t=1", "#", "?", ""}) {
+ test_get_url_query_file_name("", suffix, "");
+ test_get_url_query_file_name("/", suffix, "");
+ test_get_url_query_file_name("/a/adasd/", suffix, "");
+ test_get_url_query_file_name("/a/lklrjetn/", suffix, "adasd.asdas");
+ test_get_url_query_file_name("/", suffix, "a123asadas");
+ test_get_url_query_file_name("/", suffix, "\\a\\1\\2\\3\\a\\s\\a\\das");
+ }
+}
+
+static void test_parse_url_query(const td::string &query, const td::vector<td::string> &path,
+ const td::vector<std::pair<td::string, td::string>> &args) {
+ for (auto hash : {"", "#", "#?t=1", "#t=1&a=b"}) {
+ auto url_query = td::parse_url_query(query + hash);
+ ASSERT_EQ(path, url_query.path_);
+ ASSERT_EQ(args, url_query.args_);
+ }
+}
+
+TEST(HttpUrl, parse_url_query) {
+ test_parse_url_query("", {}, {});
+ test_parse_url_query("a", {"a"}, {});
+ test_parse_url_query("/", {}, {});
+ test_parse_url_query("//", {}, {});
+ test_parse_url_query("///?a", {}, {{"a", ""}});
+ test_parse_url_query("/a/b/c/", {"a", "b", "c"}, {});
+ test_parse_url_query("/a/b/?c/", {td::string("a"), td::string("b")}, {{"c/", ""}});
+ test_parse_url_query("?", {}, {});
+ test_parse_url_query("???", {}, {{"??", ""}});
+ test_parse_url_query("?a=b=c=d?e=f=g=h&x=y=z?d=3&", {}, {{"a", "b=c=d?e=f=g=h"}, {"x", "y=z?d=3"}});
+ test_parse_url_query("c?&&&a=b", {"c"}, {{"a", "b"}});
+ test_parse_url_query("c?&&&=b", {"c"}, {});
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/List.cpp b/protocols/Telegram/tdlib/td/tdutils/test/List.cpp
new file mode 100644
index 0000000000..02ab080d89
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/List.cpp
@@ -0,0 +1,169 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/List.h"
+#include "td/utils/MovableValue.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+#include "td/utils/TsList.h"
+
+#include <atomic>
+#include <mutex>
+#include <set>
+#include <utility>
+
+struct ListData {
+ td::MovableValue<td::uint64> value_;
+ td::MovableValue<bool> in_list_;
+
+ ListData() = default;
+ ListData(td::uint64 value, bool in_list) : value_(value), in_list_(in_list) {
+ }
+};
+
+struct Node final : public td::ListNode {
+ Node() = default;
+ explicit Node(ListData data) : data_(std::move(data)) {
+ }
+
+ ListData data_;
+};
+
+static ListData &get_data(Node &node) {
+ return node.data_;
+}
+
+static ListData &get_data(td::TsListNode<ListData> &node) {
+ return node.get_data_unsafe();
+}
+
+static std::unique_lock<std::mutex> lock(td::ListNode &node) {
+ return {};
+}
+
+static std::unique_lock<std::mutex> lock(td::TsListNode<ListData> &node) {
+ return node.lock();
+}
+
+template <class ListNodeT, class ListRootT, class NodeT>
+static void do_run_list_test(ListRootT &root, std::atomic<td::uint64> &id) {
+ td::vector<NodeT> nodes;
+
+ td::Random::Xorshift128plus rnd(123);
+
+ auto next_id = [&] {
+ return ++id;
+ };
+ auto add_node = [&] {
+ if (nodes.size() >= 20) {
+ return;
+ }
+ nodes.push_back(NodeT({next_id(), false}));
+ };
+ auto pop_node = [&] {
+ if (nodes.empty()) {
+ return;
+ }
+ nodes.pop_back();
+ };
+ auto random_node_index = [&] {
+ CHECK(!nodes.empty());
+ return rnd.fast(0, static_cast<int>(nodes.size()) - 1);
+ };
+
+ auto link_node = [&] {
+ if (nodes.empty()) {
+ return;
+ }
+ auto i = random_node_index();
+ nodes[i].remove();
+ get_data(nodes[i]) = ListData(next_id(), true);
+ root.put(&nodes[i]);
+ };
+ auto unlink_node = [&] {
+ if (nodes.empty()) {
+ return;
+ }
+ auto i = random_node_index();
+ nodes[i].remove();
+ get_data(nodes[i]).in_list_ = false;
+ };
+ auto swap_nodes = [&] {
+ if (nodes.empty()) {
+ return;
+ }
+ auto i = random_node_index();
+ auto j = random_node_index();
+ std::swap(nodes[i], nodes[j]);
+ };
+ auto set_node = [&] {
+ if (nodes.empty()) {
+ return;
+ }
+ auto i = random_node_index();
+ auto j = random_node_index();
+ nodes[i] = std::move(nodes[j]);
+ };
+ auto validate = [&] {
+ std::multiset<td::uint64> in_list;
+ std::multiset<td::uint64> not_in_list;
+ for (auto &node : nodes) {
+ if (get_data(node).in_list_.get()) {
+ in_list.insert(get_data(node).value_.get());
+ } else {
+ not_in_list.insert(get_data(node).value_.get());
+ }
+ }
+ auto guard = lock(root);
+ for (auto *begin = root.begin(), *end = root.end(); begin != end; begin = begin->get_next()) {
+ auto &data = get_data(*static_cast<NodeT *>(begin));
+ CHECK(data.in_list_.get());
+ CHECK(data.value_.get() != 0);
+ auto it = in_list.find(data.value_.get());
+ if (it != in_list.end()) {
+ in_list.erase(it);
+ } else {
+ ASSERT_EQ(0u, not_in_list.count(data.value_.get()));
+ }
+ }
+ ASSERT_EQ(0u, in_list.size());
+ };
+ td::RandomSteps steps(
+ {{add_node, 3}, {pop_node, 1}, {unlink_node, 1}, {link_node, 3}, {swap_nodes, 1}, {set_node, 1}, {validate, 1}});
+ for (int i = 0; i < 10000; i++) {
+ steps.step(rnd);
+ }
+}
+
+TEST(Misc, List) {
+ td::ListNode root;
+ std::atomic<td::uint64> id{0};
+ for (std::size_t i = 0; i < 4; i++) {
+ do_run_list_test<td::ListNode, td::ListNode, Node>(root, id);
+ }
+}
+
+TEST(Misc, TsList) {
+ td::TsList<ListData> root;
+ std::atomic<td::uint64> id{0};
+ for (std::size_t i = 0; i < 4; i++) {
+ do_run_list_test<td::TsListNode<ListData>, td::TsList<ListData>, td::TsListNode<ListData>>(root, id);
+ }
+}
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(Misc, TsListConcurrent) {
+ td::TsList<ListData> root;
+ td::vector<td::thread> threads;
+ std::atomic<td::uint64> id{0};
+ for (std::size_t i = 0; i < 4; i++) {
+ threads.emplace_back(
+ [&] { do_run_list_test<td::TsListNode<ListData>, td::TsList<ListData>, td::TsListNode<ListData>>(root, id); });
+ }
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp b/protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp
index 2da3f0cd3f..c038303c37 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/MpmcQueue.cpp
@@ -1,9 +1,10 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
+#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/MpmcQueue.h"
#include "td/utils/port/thread.h"
@@ -49,7 +50,7 @@ TEST(OneValue, stress) {
std::vector<td::thread> threads;
td::OneValue<std::string> value;
for (size_t i = 0; i < 2; i++) {
- threads.push_back(td::thread([&, id = i] {
+ threads.emplace_back([&, id = i] {
for (td::uint64 round = 1; round < 100000; round++) {
if (id == 0) {
value.reset();
@@ -68,7 +69,7 @@ TEST(OneValue, stress) {
if (set_status) {
CHECK(get_status);
CHECK(from.empty());
- CHECK(to == "hello") << to;
+ LOG_CHECK(to == "hello") << to;
} else {
CHECK(!get_status);
CHECK(from == "hello");
@@ -76,17 +77,17 @@ TEST(OneValue, stress) {
}
}
}
- }));
+ });
}
for (auto &thread : threads) {
thread.join();
}
}
-#endif //!TD_THREAD_UNSUPPORTED
+#endif
TEST(MpmcQueueBlock, simple) {
// Test doesn't work now and it is ok, try_pop, logic changed
- return;
+ /*
td::MpmcQueueBlock<std::string> block(2);
std::string x = "hello";
using PushStatus = td::MpmcQueueBlock<std::string>::PushStatus;
@@ -111,6 +112,7 @@ TEST(MpmcQueueBlock, simple) {
CHECK(pop_status == PopStatus::Ok);
pop_status = block.try_pop(x);
CHECK(pop_status == PopStatus::Closed);
+ */
}
TEST(MpmcQueue, simple) {
@@ -121,7 +123,7 @@ TEST(MpmcQueue, simple) {
}
for (int i = 0; i < 100; i++) {
int x = q.pop(0);
- CHECK(x == i) << x << " expected " << i;
+ LOG_CHECK(x == i) << x << " expected " << i;
}
}
}
@@ -188,18 +190,18 @@ TEST(MpmcQueue, multi_thread) {
from[data.from] = data.value;
}
}
- CHECK(all.size() == n * qn) << all.size();
+ LOG_CHECK(all.size() == n * qn) << all.size();
std::sort(all.begin(), all.end(),
[](const auto &a, const auto &b) { return std::tie(a.from, a.value) < std::tie(b.from, b.value); });
for (size_t i = 0; i < n * qn; i++) {
CHECK(all[i].from == i / qn);
CHECK(all[i].value == i % qn + 1);
}
- LOG(ERROR) << "Undeleted pointers: " << q.hazard_pointers_to_delele_size_unsafe();
+ LOG(INFO) << "Undeleted pointers: " << q.hazard_pointers_to_delele_size_unsafe();
CHECK(q.hazard_pointers_to_delele_size_unsafe() <= (n + m + 1) * (n + m + 1));
for (size_t id = 0; id < n + m + 1; id++) {
q.gc(id);
}
- CHECK(q.hazard_pointers_to_delele_size_unsafe() == 0) << q.hazard_pointers_to_delele_size_unsafe();
+ LOG_CHECK(q.hazard_pointers_to_delele_size_unsafe() == 0) << q.hazard_pointers_to_delele_size_unsafe();
}
-#endif //!TD_THREAD_UNSUPPORTED
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp b/protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp
index e27e217713..4ac882dcaf 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/MpmcWaiter.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -13,21 +13,22 @@
#include <atomic>
#if !TD_THREAD_UNSUPPORTED
-TEST(MpmcWaiter, stress_one_one) {
+template <class W>
+static void test_waiter_stress_one_one() {
td::Stage run;
td::Stage check;
std::vector<td::thread> threads;
- std::atomic<size_t> value;
+ std::atomic<size_t> value{0};
size_t write_cnt = 10;
- std::unique_ptr<td::MpmcWaiter> waiter;
+ td::unique_ptr<W> waiter;
size_t threads_n = 2;
for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
for (td::uint64 round = 1; round < 100000; round++) {
if (id == 0) {
value = 0;
- waiter = std::make_unique<td::MpmcWaiter>();
+ waiter = td::make_unique<W>();
write_cnt = td::Random::fast(1, 10);
}
run.wait(round * threads_n);
@@ -37,17 +38,19 @@ TEST(MpmcWaiter, stress_one_one) {
waiter->notify();
}
} else {
- int yields = 0;
+ typename W::Slot slot;
+ W::init_slot(slot, id);
for (size_t i = 1; i <= write_cnt; i++) {
while (true) {
auto x = value.load(std::memory_order_relaxed);
if (x >= i) {
break;
}
- yields = waiter->wait(yields, id);
+ waiter->wait(slot);
}
- yields = waiter->stop_wait(yields, id);
+ waiter->stop_wait(slot);
}
+ waiter->stop_wait(slot);
}
check.wait(round * threads_n);
}
@@ -57,19 +60,29 @@ TEST(MpmcWaiter, stress_one_one) {
thread.join();
}
}
-TEST(MpmcWaiter, stress) {
+
+TEST(MpmcEagerWaiter, stress_one_one) {
+ test_waiter_stress_one_one<td::MpmcEagerWaiter>();
+}
+
+TEST(MpmcSleepyWaiter, stress_one_one) {
+ test_waiter_stress_one_one<td::MpmcSleepyWaiter>();
+}
+
+template <class W>
+static void test_waiter_stress() {
td::Stage run;
td::Stage check;
std::vector<td::thread> threads;
size_t write_n;
size_t read_n;
- std::atomic<size_t> write_pos;
- std::atomic<size_t> read_pos;
+ std::atomic<size_t> write_pos{0};
+ std::atomic<size_t> read_pos{0};
size_t end_pos;
size_t write_cnt;
size_t threads_n = 20;
- std::unique_ptr<td::MpmcWaiter> waiter;
+ td::unique_ptr<W> waiter;
for (size_t i = 0; i < threads_n; i++) {
threads.push_back(td::thread([&, id = static_cast<td::uint32>(i)] {
for (td::uint64 round = 1; round < 1000; round++) {
@@ -80,7 +93,7 @@ TEST(MpmcWaiter, stress) {
end_pos = write_n * write_cnt;
write_pos = 0;
read_pos = 0;
- waiter = std::make_unique<td::MpmcWaiter>();
+ waiter = td::make_unique<W>();
}
run.wait(round * threads_n);
if (id <= write_n) {
@@ -92,21 +105,26 @@ TEST(MpmcWaiter, stress) {
waiter->notify();
}
} else if (id > 10 && id - 10 <= read_n) {
- int yields = 0;
+ typename W::Slot slot;
+ W::init_slot(slot, id);
while (true) {
auto x = read_pos.load(std::memory_order_relaxed);
if (x == end_pos) {
+ waiter->stop_wait(slot);
break;
}
if (x == write_pos.load(std::memory_order_relaxed)) {
- yields = waiter->wait(yields, id);
+ waiter->wait(slot);
continue;
}
- yields = waiter->stop_wait(yields, id);
+ waiter->stop_wait(slot);
read_pos.compare_exchange_strong(x, x + 1, std::memory_order_relaxed);
}
}
check.wait(round * threads_n);
+ if (id == 0) {
+ waiter->close();
+ }
}
}));
}
@@ -114,4 +132,12 @@ TEST(MpmcWaiter, stress) {
thread.join();
}
}
-#endif // !TD_THREAD_UNSUPPORTED
+
+TEST(MpmcEagerWaiter, stress_multi) {
+ test_waiter_stress<td::MpmcEagerWaiter>();
+}
+
+TEST(MpmcSleepyWaiter, stress_multi) {
+ test_waiter_stress<td::MpmcSleepyWaiter>();
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp b/protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp
index 629e5b7223..43b0ccf086 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/MpscLinkQueue.cpp
@@ -1,16 +1,17 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
+#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/MpscLinkQueue.h"
#include "td/utils/port/thread.h"
#include "td/utils/tests.h"
-class NodeX : public td::MpscLinkQueueImpl::Node {
+class NodeX final : public td::MpscLinkQueueImpl::Node {
public:
explicit NodeX(int value) : value_(value) {
}
@@ -29,8 +30,8 @@ class NodeX : public td::MpscLinkQueueImpl::Node {
};
using QueueNode = td::MpscLinkQueueUniquePtrNode<NodeX>;
-QueueNode create_node(int value) {
- return QueueNode(std::make_unique<NodeX>(value));
+static QueueNode create_node(int value) {
+ return QueueNode(td::make_unique<NodeX>(value));
}
TEST(MpscLinkQueue, one_thread) {
@@ -48,7 +49,7 @@ TEST(MpscLinkQueue, one_thread) {
while (auto node = reader.read()) {
v.push_back(node.value().value());
}
- CHECK((v == std::vector<int>{1, 2, 3, 4})) << td::format::as_array(v);
+ LOG_CHECK((v == std::vector<int>{1, 2, 3, 4})) << td::format::as_array(v);
v.clear();
queue.push(create_node(5));
@@ -56,7 +57,7 @@ TEST(MpscLinkQueue, one_thread) {
while (auto node = reader.read()) {
v.push_back(node.value().value());
}
- CHECK((v == std::vector<int>{5})) << td::format::as_array(v);
+ LOG_CHECK((v == std::vector<int>{5})) << td::format::as_array(v);
}
{
@@ -70,7 +71,7 @@ TEST(MpscLinkQueue, one_thread) {
while (auto node = reader.read()) {
v.push_back(node.value().value());
}
- CHECK((v == std::vector<int>{3, 2, 1, 0})) << td::format::as_array(v);
+ LOG_CHECK((v == std::vector<int>{3, 2, 1, 0})) << td::format::as_array(v);
}
}
@@ -112,4 +113,4 @@ TEST(MpscLinkQueue, multi_thread) {
thread.join();
}
}
-#endif //!TD_THREAD_UNSUPPORTED
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/OptionParser.cpp b/protocols/Telegram/tdlib/td/tdutils/test/OptionParser.cpp
new file mode 100644
index 0000000000..8600eb9f19
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/OptionParser.cpp
@@ -0,0 +1,82 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/misc.h"
+#include "td/utils/OptionParser.h"
+#include "td/utils/Slice.h"
+#include "td/utils/tests.h"
+
+TEST(OptionParser, run) {
+ td::OptionParser options;
+ options.set_description("test description");
+
+ td::string exename = "exename";
+ td::vector<td::string> args;
+ auto run_option_parser = [&](td::string command_line) {
+ args = td::full_split(std::move(command_line), ' ');
+ td::vector<char *> argv;
+ argv.push_back(&exename[0]);
+ for (auto &arg : args) {
+ argv.push_back(&arg[0]);
+ }
+ return options.run_impl(static_cast<int>(argv.size()), &argv[0], -1);
+ };
+
+ td::uint64 chosen_options = 0;
+ td::vector<td::string> chosen_parameters;
+ auto test_success = [&](td::string command_line, td::uint64 expected_options,
+ const td::vector<td::string> &expected_parameters,
+ const td::vector<td::string> &expected_result) {
+ chosen_options = 0;
+ chosen_parameters.clear();
+ auto result = run_option_parser(std::move(command_line));
+ ASSERT_TRUE(result.is_ok());
+ ASSERT_EQ(expected_options, chosen_options);
+ ASSERT_EQ(expected_parameters, chosen_parameters);
+ ASSERT_EQ(expected_result.size(), result.ok().size());
+ for (size_t i = 0; i < expected_result.size(); i++) {
+ ASSERT_STREQ(expected_result[i], td::string(result.ok()[i]));
+ }
+ };
+ auto test_fail = [&](td::string command_line) {
+ auto result = run_option_parser(std::move(command_line));
+ ASSERT_TRUE(result.is_error());
+ };
+
+ options.add_option('q', "", "", [&] { chosen_options += 1; });
+ options.add_option('\0', "http-port2", "", [&] { chosen_options += 10; });
+ options.add_option('p', "http-port", "", [&](td::Slice parameter) {
+ chosen_options += 100;
+ chosen_parameters.push_back(parameter.str());
+ });
+ options.add_option('v', "test", "", [&] { chosen_options += 1000; });
+
+ test_fail("-http-port2");
+ test_success("-", 0, {}, {"-"});
+ test_fail("--http-port");
+ test_fail("--http-port3");
+ test_fail("--http-por");
+ test_fail("--http-port2=1");
+ test_fail("--q");
+ test_fail("-qvp");
+ test_fail("-p");
+ test_fail("-u");
+ test_success("-q", 1, {}, {});
+ test_success("-vvvvvvvvvv", 10000, {}, {});
+ test_success("-qpv", 101, {"v"}, {});
+ test_success("-qp -v", 101, {"-v"}, {});
+ test_success("-qp --http-port2", 101, {"--http-port2"}, {});
+ test_success("-qp -- -v", 1101, {"--"}, {});
+ test_success("-qvqvpqv", 2102, {"qv"}, {});
+ test_success("aba --http-port2 caba --http-port2 dabacaba", 20, {}, {"aba", "caba", "dabacaba"});
+ test_success("das -pqwerty -- -v asd --http-port", 100, {"qwerty"}, {"das", "-v", "asd", "--http-port"});
+ test_success("-p option --http-port option2 --http-port=option3 --http-port=", 400,
+ {"option", "option2", "option3", ""}, {});
+ test_success("", 0, {}, {});
+ test_success("a", 0, {}, {"a"});
+ test_success("", 0, {}, {});
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp b/protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp
index 6a5a20015f..c5c963bedc 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/OrderedEventsProcessor.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -18,8 +18,8 @@ TEST(OrderedEventsProcessor, random) {
int offset = 1000000;
std::vector<std::pair<int, int>> v;
for (int i = 0; i < n; i++) {
- auto shift = td::Random::fast(0, 1) ? td::Random::fast(0, d) : td::Random::fast(0, 1) * d;
- v.push_back({i + shift, i + offset});
+ auto shift = td::Random::fast_bool() ? td::Random::fast(0, d) : td::Random::fast(0, 1) * d;
+ v.emplace_back(i + shift, i + offset);
}
std::sort(v.begin(), v.end());
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp b/protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp
index 61d956f4e6..a4762e25f3 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/SharedObjectPool.cpp
@@ -1,10 +1,10 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
-#include "td/utils/logging.h"
+#include "td/utils/common.h"
#include "td/utils/SharedObjectPool.h"
#include "td/utils/tests.h"
@@ -56,7 +56,16 @@ TEST(SharedPtr, simple) {
ptr2 = std::move(ptr);
CHECK(ptr.empty());
CHECK(*ptr2 == "hello");
+#if TD_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunknown-pragmas"
+#pragma clang diagnostic ignored "-Wunknown-warning-option"
+#pragma clang diagnostic ignored "-Wself-assign-overloaded"
+#endif
ptr2 = ptr2;
+#if TD_CLANG
+#pragma clang diagnostic pop
+#endif
CHECK(*ptr2 == "hello");
CHECK(!Deleter::was_delete());
ptr2.reset();
@@ -80,15 +89,15 @@ TEST(SharedObjectPool, simple) {
};
{
td::SharedObjectPool<Node> pool;
- pool.alloc();
- pool.alloc();
- pool.alloc();
- pool.alloc();
- pool.alloc();
+ { auto ptr1 = pool.alloc(); }
+ { auto ptr2 = pool.alloc(); }
+ { auto ptr3 = pool.alloc(); }
+ { auto ptr4 = pool.alloc(); }
+ { auto ptr5 = pool.alloc(); }
CHECK(Node::cnt() == 0);
CHECK(pool.total_size() == 1);
CHECK(pool.calc_free_size() == 1);
- pool.alloc(), pool.alloc(), pool.alloc();
+ { auto ptr6 = pool.alloc(), ptr7 = pool.alloc(), ptr8 = pool.alloc(); }
CHECK(pool.total_size() == 3);
CHECK(pool.calc_free_size() == 3);
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/SharedSlice.cpp b/protocols/Telegram/tdlib/td/tdutils/test/SharedSlice.cpp
new file mode 100644
index 0000000000..7327f0dbb3
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/SharedSlice.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/SharedSlice.h"
+#include "td/utils/tests.h"
+
+char disable_linker_warning_about_empty_file_tdutils_test_shared_slice_cpp TD_UNUSED;
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(SharedSlice, Hands) {
+ {
+ td::SharedSlice h("hello");
+ ASSERT_EQ("hello", h.as_slice());
+ // auto g = h; // CE
+ auto g = h.clone();
+ ASSERT_EQ("hello", h.as_slice());
+ ASSERT_EQ("hello", g.as_slice());
+ }
+
+ {
+ td::SharedSlice h("hello");
+ td::UniqueSharedSlice g(std::move(h));
+ ASSERT_EQ("", h.as_slice());
+ ASSERT_EQ("hello", g.as_slice());
+ }
+ {
+ td::SharedSlice h("hello");
+ td::SharedSlice t = h.clone();
+ td::UniqueSharedSlice g(std::move(h));
+ ASSERT_EQ("", h.as_slice());
+ ASSERT_EQ("hello", g.as_slice());
+ ASSERT_EQ("hello", t.as_slice());
+ }
+
+ {
+ td::UniqueSharedSlice g(5);
+ g.as_mutable_slice().copy_from("hello");
+ td::SharedSlice h(std::move(g));
+ ASSERT_EQ("hello", h);
+ ASSERT_EQ("", g);
+ }
+
+ {
+ td::UniqueSlice h("hello");
+ td::UniqueSlice g(std::move(h));
+ ASSERT_EQ("", h.as_slice());
+ ASSERT_EQ("hello", g.as_slice());
+ }
+
+ {
+ td::SecureString h("hello");
+ td::SecureString g(std::move(h));
+ ASSERT_EQ("", h.as_slice());
+ ASSERT_EQ("hello", g.as_slice());
+ }
+
+ {
+ td::Stage stage;
+ td::SharedSlice a;
+ td::SharedSlice b;
+ td::vector<td::thread> threads(2);
+ for (int i = 0; i < 2; i++) {
+ threads[i] = td::thread([i, &stage, &a, &b] {
+ for (int j = 0; j < 10000; j++) {
+ if (i == 0) {
+ a = td::SharedSlice("hello");
+ b = a.clone();
+ }
+ stage.wait((2 * j + 1) * 2);
+ if (i == 0) {
+ ASSERT_EQ('h', a[0]);
+ a.clear();
+ } else {
+ td::UniqueSharedSlice c(std::move(b));
+ c.as_mutable_slice()[0] = '!';
+ }
+ stage.wait((2 * j + 2) * 2);
+ }
+ });
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+ }
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/StealingQueue.cpp b/protocols/Telegram/tdlib/td/tdutils/test/StealingQueue.cpp
new file mode 100644
index 0000000000..453a63179f
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/StealingQueue.cpp
@@ -0,0 +1,180 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/AtomicRead.h"
+#include "td/utils/benchmark.h"
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/MpmcQueue.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Random.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/StealingQueue.h"
+#include "td/utils/tests.h"
+
+#include <atomic>
+#include <cstring>
+
+TEST(StealingQueue, very_simple) {
+ td::StealingQueue<int, 8> q;
+ q.local_push(1, [](auto x) { UNREACHABLE(); });
+ int x;
+ CHECK(q.local_pop(x));
+ ASSERT_EQ(1, x);
+}
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(AtomicRead, simple) {
+ td::Stage run;
+ td::Stage check;
+
+ std::size_t threads_n = 10;
+ td::vector<td::thread> threads;
+
+ int x{0};
+ std::atomic<int> version{0};
+
+ td::int64 res = 0;
+ for (std::size_t i = 0; i < threads_n; i++) {
+ threads.emplace_back([&, id = static_cast<td::uint32>(i)] {
+ for (td::uint64 round = 1; round < 10000; round++) {
+ run.wait(round * threads_n);
+ if (id == 0) {
+ version++;
+ x++;
+ version++;
+ } else {
+ int y = 0;
+ auto v1 = version.load();
+ y = x;
+ auto v2 = version.load();
+ if (v1 == v2 && v1 % 2 == 0) {
+ res += y;
+ }
+ }
+
+ check.wait(round * threads_n);
+ }
+ });
+ }
+ td::do_not_optimize_away(res);
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+
+TEST(AtomicRead, simple2) {
+ td::Stage run;
+ td::Stage check;
+
+ std::size_t threads_n = 10;
+ td::vector<td::thread> threads;
+
+ struct Value {
+ td::uint64 value = 0;
+ char str[50] = "0 0 0 0";
+ };
+ td::AtomicRead<Value> value;
+
+ auto to_str = [](td::uint64 i) {
+ return PSTRING() << i << " " << i << " " << i << " " << i;
+ };
+ for (std::size_t i = 0; i < threads_n; i++) {
+ threads.emplace_back([&, id = static_cast<td::uint32>(i)] {
+ for (td::uint64 round = 1; round < 10000; round++) {
+ run.wait(round * threads_n);
+ if (id == 0) {
+ auto x = value.lock();
+ x->value = round;
+ auto str = to_str(round);
+ std::memcpy(x->str, str.c_str(), str.size() + 1);
+ } else {
+ Value x;
+ value.read(x);
+ LOG_CHECK(x.value == round || x.value == round - 1) << x.value << " " << round;
+ CHECK(x.str == to_str(x.value));
+ }
+ check.wait(round * threads_n);
+ }
+ });
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+
+TEST(StealingQueue, simple) {
+ td::uint64 sum = 0;
+ std::atomic<td::uint64> got_sum{0};
+
+ td::Stage run;
+ td::Stage check;
+
+ std::size_t threads_n = 10;
+ td::vector<td::thread> threads;
+ td::vector<td::StealingQueue<int, 8>> lq(threads_n);
+ td::MpmcQueue<int> gq(threads_n);
+
+ constexpr td::uint64 XN = 20;
+ td::uint64 x_sum[XN];
+ x_sum[0] = 0;
+ x_sum[1] = 1;
+ for (td::uint64 i = 2; i < XN; i++) {
+ x_sum[i] = i + x_sum[i - 1] + x_sum[i - 2];
+ }
+
+ td::Random::Xorshift128plus rnd(123);
+ for (std::size_t i = 0; i < threads_n; i++) {
+ threads.emplace_back([&, id = static_cast<td::uint32>(i)] {
+ for (td::uint64 round = 1; round < 1000; round++) {
+ if (id == 0) {
+ sum = 0;
+ auto n = static_cast<int>(rnd() % 5);
+ for (int j = 0; j < n; j++) {
+ auto x = static_cast<int>(rnd() % XN);
+ sum += x_sum[x];
+ gq.push(x, id);
+ }
+ got_sum = 0;
+ }
+ run.wait(round * threads_n);
+ while (got_sum.load() != sum) {
+ auto x = [&] {
+ int res;
+ if (lq[id].local_pop(res)) {
+ return res;
+ }
+ if (gq.try_pop(res, id)) {
+ return res;
+ }
+ if (lq[id].steal(res, lq[static_cast<size_t>(rnd()) % threads_n])) {
+ //LOG(ERROR) << "STEAL";
+ return res;
+ }
+ return 0;
+ }();
+ if (x == 0) {
+ continue;
+ }
+ //LOG(ERROR) << x << " " << got_sum.load() << " " << sum;
+ got_sum.fetch_add(x, std::memory_order_relaxed);
+ lq[id].local_push(x - 1, [&](auto y) {
+ //LOG(ERROR) << "OVERFLOW";
+ gq.push(y, id);
+ });
+ if (x > 1) {
+ lq[id].local_push(x - 2, [&](auto y) { gq.push(y, id); });
+ }
+ }
+ check.wait(round * threads_n);
+ }
+ });
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashMap.cpp b/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashMap.cpp
new file mode 100644
index 0000000000..38def77772
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashMap.cpp
@@ -0,0 +1,95 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/FlatHashMap.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+#include "td/utils/WaitFreeHashMap.h"
+
+TEST(WaitFreeHashMap, stress_test) {
+ td::Random::Xorshift128plus rnd(123);
+ td::FlatHashMap<td::uint64, td::uint64> reference;
+ td::WaitFreeHashMap<td::uint64, td::uint64> map;
+
+ td::vector<td::RandomSteps::Step> steps;
+ auto add_step = [&](td::uint32 weight, auto f) {
+ steps.emplace_back(td::RandomSteps::Step{std::move(f), weight});
+ };
+
+ auto gen_key = [&] {
+ return rnd() % 100000 + 1;
+ };
+
+ auto check = [&](bool check_size = false) {
+ if (check_size) {
+ ASSERT_EQ(reference.size(), map.calc_size());
+ }
+ ASSERT_EQ(reference.empty(), map.empty());
+
+ if (reference.size() < 100) {
+ td::uint64 result = 0;
+ for (auto &it : reference) {
+ result += it.first * 101;
+ result += it.second;
+ }
+ map.foreach([&](const td::uint64 &key, td::uint64 &value) {
+ result -= key * 101;
+ result -= value;
+ });
+ ASSERT_EQ(0u, result);
+ }
+ };
+
+ add_step(2000, [&] {
+ auto key = gen_key();
+ auto value = rnd();
+ reference[key] = value;
+ if (td::Random::fast_bool()) {
+ map.set(key, value);
+ } else {
+ map[key] = value;
+ }
+ ASSERT_EQ(reference[key], map.get(key));
+ check();
+ });
+
+ add_step(200, [&] {
+ auto key = gen_key();
+ ASSERT_EQ(reference[key], map[key]);
+ check(true);
+ });
+
+ add_step(2000, [&] {
+ auto key = gen_key();
+ auto ref_it = reference.find(key);
+ auto ref_value = ref_it == reference.end() ? 0 : ref_it->second;
+ ASSERT_EQ(ref_value, map.get(key));
+ check();
+ });
+
+ add_step(500, [&] {
+ auto key = gen_key();
+ size_t reference_erased_count = reference.erase(key);
+ size_t map_erased_count = map.erase(key);
+ ASSERT_EQ(reference_erased_count, map_erased_count);
+ check();
+ });
+
+ td::RandomSteps runner(std::move(steps));
+ for (size_t i = 0; i < 1000000; i++) {
+ runner.step(rnd);
+ }
+
+ for (size_t test = 0; test < 1000; test++) {
+ reference = {};
+ map = {};
+
+ for (size_t i = 0; i < 100; i++) {
+ runner.step(rnd);
+ }
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashSet.cpp b/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashSet.cpp
new file mode 100644
index 0000000000..ec4096c850
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeHashSet.cpp
@@ -0,0 +1,73 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/FlatHashSet.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+#include "td/utils/WaitFreeHashSet.h"
+
+TEST(WaitFreeHashSet, stress_test) {
+ td::Random::Xorshift128plus rnd(123);
+ td::FlatHashSet<td::uint64> reference;
+ td::WaitFreeHashSet<td::uint64> set;
+
+ td::vector<td::RandomSteps::Step> steps;
+ auto add_step = [&](td::uint32 weight, auto f) {
+ steps.emplace_back(td::RandomSteps::Step{std::move(f), weight});
+ };
+
+ auto gen_key = [&] {
+ return rnd() % 100000 + 1;
+ };
+
+ auto check = [&](bool check_size = false) {
+ if (check_size) {
+ ASSERT_EQ(reference.size(), set.calc_size());
+ }
+ ASSERT_EQ(reference.empty(), set.empty());
+
+ if (reference.size() < 100) {
+ td::uint64 result = 0;
+ for (auto &it : reference) {
+ result += it * 101;
+ }
+ set.foreach([&](const td::uint64 &key) { result -= key * 101; });
+ ASSERT_EQ(0u, result);
+ }
+ };
+
+ add_step(2000, [&] {
+ auto key = gen_key();
+ ASSERT_EQ(reference.count(key), set.count(key));
+ reference.insert(key);
+ set.insert(key);
+ ASSERT_EQ(reference.count(key), set.count(key));
+ check();
+ });
+
+ add_step(500, [&] {
+ auto key = gen_key();
+ size_t reference_erased_count = reference.erase(key);
+ size_t set_erased_count = set.erase(key);
+ ASSERT_EQ(reference_erased_count, set_erased_count);
+ check();
+ });
+
+ td::RandomSteps runner(std::move(steps));
+ for (size_t i = 0; i < 1000000; i++) {
+ runner.step(rnd);
+ }
+
+ for (size_t test = 0; test < 1000; test++) {
+ reference = {};
+ set = {};
+
+ for (size_t i = 0; i < 100; i++) {
+ runner.step(rnd);
+ }
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeVector.cpp b/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeVector.cpp
new file mode 100644
index 0000000000..0f0cc58796
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/WaitFreeVector.cpp
@@ -0,0 +1,69 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/Random.h"
+#include "td/utils/tests.h"
+#include "td/utils/WaitFreeVector.h"
+
+TEST(WaitFreeVector, stress_test) {
+ td::Random::Xorshift128plus rnd(123);
+ td::vector<td::uint64> reference;
+ td::WaitFreeVector<td::uint64> vector;
+
+ td::vector<td::RandomSteps::Step> steps;
+ auto add_step = [&](td::uint32 weight, auto f) {
+ steps.emplace_back(td::RandomSteps::Step{std::move(f), weight});
+ };
+
+ auto gen_key = [&] {
+ return static_cast<size_t>(rnd() % reference.size());
+ };
+
+ add_step(2000, [&] {
+ ASSERT_EQ(reference.size(), vector.size());
+ ASSERT_EQ(reference.empty(), vector.empty());
+ if (reference.empty()) {
+ return;
+ }
+ auto key = gen_key();
+ ASSERT_EQ(reference[key], vector[key]);
+ auto value = rnd();
+ reference[key] = value;
+ vector[key] = value;
+ ASSERT_EQ(reference[key], vector[key]);
+ });
+
+ add_step(2000, [&] {
+ ASSERT_EQ(reference.size(), vector.size());
+ ASSERT_EQ(reference.empty(), vector.empty());
+ auto value = rnd();
+ reference.emplace_back(value);
+ if (rnd() & 1) {
+ vector.emplace_back(value);
+ } else if (rnd() & 1) {
+ vector.push_back(value);
+ } else {
+ vector.push_back(std::move(value));
+ }
+ ASSERT_EQ(reference.back(), vector.back());
+ });
+
+ add_step(500, [&] {
+ ASSERT_EQ(reference.size(), vector.size());
+ ASSERT_EQ(reference.empty(), vector.empty());
+ if (reference.empty()) {
+ return;
+ }
+ reference.pop_back();
+ vector.pop_back();
+ });
+
+ td::RandomSteps runner(std::move(steps));
+ for (size_t i = 0; i < 1000000; i++) {
+ runner.step(rnd);
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/bitmask.cpp b/protocols/Telegram/tdlib/td/tdutils/test/bitmask.cpp
new file mode 100644
index 0000000000..e81c406bbe
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/bitmask.cpp
@@ -0,0 +1,249 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/common.h"
+#include "td/utils/misc.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Status.h"
+#include "td/utils/tests.h"
+#include "td/utils/utf8.h"
+
+#include <algorithm>
+
+namespace td {
+
+class RangeSet {
+ template <class T>
+ static auto find(T &ranges, int64 begin) {
+ return std::lower_bound(ranges.begin(), ranges.end(), begin,
+ [](const Range &range, int64 begin) { return range.end < begin; });
+ }
+ auto find(int64 begin) const {
+ return find(ranges_, begin);
+ }
+ auto find(int64 begin) {
+ return find(ranges_, begin);
+ }
+
+ public:
+ struct Range {
+ int64 begin;
+ int64 end;
+ };
+
+ static constexpr int64 BIT_SIZE = 1024;
+
+ static RangeSet create_one_range(int64 end, int64 begin = 0) {
+ RangeSet res;
+ res.ranges_.push_back({begin, end});
+ return res;
+ }
+ static Result<RangeSet> decode(CSlice data) {
+ if (!check_utf8(data)) {
+ return Status::Error("Invalid encoding");
+ }
+ uint32 curr = 0;
+ bool is_empty = false;
+ RangeSet res;
+ for (auto begin = data.ubegin(); begin != data.uend();) {
+ uint32 size;
+ begin = next_utf8_unsafe(begin, &size);
+
+ if (!is_empty && size != 0) {
+ res.ranges_.push_back({curr * BIT_SIZE, (curr + size) * BIT_SIZE});
+ }
+ curr += size;
+ is_empty = !is_empty;
+ }
+ return res;
+ }
+
+ string encode(int64 prefix_size = -1) const {
+ vector<uint32> sizes;
+ uint32 all_end = 0;
+
+ if (prefix_size != -1) {
+ prefix_size = (prefix_size + BIT_SIZE - 1) / BIT_SIZE * BIT_SIZE;
+ }
+ for (auto it : ranges_) {
+ if (prefix_size != -1 && it.begin >= prefix_size) {
+ break;
+ }
+ if (prefix_size != -1 && it.end > prefix_size) {
+ it.end = prefix_size;
+ }
+
+ CHECK(it.begin % BIT_SIZE == 0);
+ CHECK(it.end % BIT_SIZE == 0);
+ auto begin = narrow_cast<uint32>(it.begin / BIT_SIZE);
+ auto end = narrow_cast<uint32>(it.end / BIT_SIZE);
+ if (sizes.empty()) {
+ if (begin != 0) {
+ sizes.push_back(0);
+ sizes.push_back(begin);
+ }
+ } else {
+ sizes.push_back(begin - all_end);
+ }
+ sizes.push_back(end - begin);
+ all_end = end;
+ }
+
+ string res;
+ for (auto c : sizes) {
+ append_utf8_character(res, c);
+ }
+ return res;
+ }
+
+ int64 get_ready_prefix_size(int64 offset, int64 file_size = -1) const {
+ auto it = find(offset);
+ if (it == ranges_.end()) {
+ return 0;
+ }
+ if (it->begin > offset) {
+ return 0;
+ }
+ CHECK(offset >= it->begin);
+ CHECK(offset <= it->end);
+ auto end = it->end;
+ if (file_size != -1 && end > file_size) {
+ end = file_size;
+ }
+ if (end < offset) {
+ return 0;
+ }
+ return end - offset;
+ }
+ int64 get_total_size(int64 file_size) const {
+ int64 res = 0;
+ for (auto it : ranges_) {
+ if (it.begin >= file_size) {
+ break;
+ }
+ if (it.end > file_size) {
+ it.end = file_size;
+ }
+ res += it.end - it.begin;
+ }
+ return res;
+ }
+ int64 get_ready_parts(int64 offset_part, int32 part_size) const {
+ auto offset = offset_part * part_size;
+ auto it = find(offset);
+ if (it == ranges_.end()) {
+ return 0;
+ }
+ if (it->begin > offset) {
+ return 0;
+ }
+ return (it->end - offset) / part_size;
+ }
+
+ bool is_ready(int64 begin, int64 end) const {
+ auto it = find(begin);
+ if (it == ranges_.end()) {
+ return false;
+ }
+ return it->begin <= begin && end <= it->end;
+ }
+
+ void set(int64 begin, int64 end) {
+ CHECK(begin % BIT_SIZE == 0);
+ CHECK(end % BIT_SIZE == 0);
+ // 1. skip all with r.end < begin
+ auto it_begin = find(begin);
+
+ // 2. combine with all r.begin <= end
+ auto it_end = it_begin;
+ for (; it_end != ranges_.end() && it_end->begin <= end; ++it_end) {
+ }
+
+ if (it_begin == it_end) {
+ ranges_.insert(it_begin, Range{begin, end});
+ } else {
+ begin = td::min(begin, it_begin->begin);
+ --it_end;
+ end = td::max(end, it_end->end);
+ *it_end = Range{begin, end};
+ ranges_.erase(it_begin, it_end);
+ }
+ }
+
+ vector<int32> as_vector(int32 part_size) const {
+ vector<int32> res;
+ for (const auto &it : ranges_) {
+ auto begin = narrow_cast<int32>((it.begin + part_size - 1) / part_size);
+ auto end = narrow_cast<int32>(it.end / part_size);
+ while (begin < end) {
+ res.push_back(begin++);
+ }
+ }
+ return res;
+ }
+
+ private:
+ vector<Range> ranges_;
+};
+
+TEST(Bitmask, simple) {
+ auto validate_encoding = [](auto &rs) {
+ auto str = rs.encode();
+ RangeSet rs2 = RangeSet::decode(str).move_as_ok();
+ auto str2 = rs2.encode();
+ rs = std::move(rs2);
+ CHECK(str2 == str);
+ };
+ {
+ RangeSet rs;
+ int32 S = 128 * 1024;
+ int32 O = S * 5000;
+ for (int i = 1; i < 30; i++) {
+ if (i % 2 == 0) {
+ rs.set(O + S * i, O + S * (i + 1));
+ }
+ }
+ validate_encoding(rs);
+ }
+ {
+ RangeSet rs;
+ int32 S = 1024;
+ auto get = [&](auto p) {
+ return rs.get_ready_prefix_size(p * S) / S;
+ };
+ auto set = [&](auto l, auto r) {
+ rs.set(l * S, r * S);
+ validate_encoding(rs);
+ ASSERT_TRUE(rs.is_ready(l * S, r * S));
+ ASSERT_TRUE(get(l) >= (r - l));
+ };
+ set(6, 7);
+ ASSERT_EQ(1, get(6));
+ ASSERT_EQ(0, get(5));
+ set(8, 9);
+ ASSERT_EQ(0, get(7));
+ set(7, 8);
+ ASSERT_EQ(2, get(7));
+ ASSERT_EQ(3, get(6));
+ set(3, 5);
+ ASSERT_EQ(1, get(4));
+ set(4, 6);
+ ASSERT_EQ(5, get(4));
+ set(10, 11);
+ set(9, 10);
+ ASSERT_EQ(8, get(3));
+ set(14, 16);
+ set(12, 13);
+ ASSERT_EQ(8, get(3));
+
+ ASSERT_EQ(10, rs.get_ready_prefix_size(S * 3, S * 3 + 10));
+ ASSERT_TRUE(!rs.is_ready(S * 11, S * 12));
+ ASSERT_EQ(3, rs.get_ready_parts(2, S * 2));
+ ASSERT_EQ(vector<int32>({2, 3, 4, 7}), rs.as_vector(S * 2));
+ }
+}
+
+} // namespace td
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/buffer.cpp b/protocols/Telegram/tdlib/td/tdutils/test/buffer.cpp
new file mode 100644
index 0000000000..4bc406cc64
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/buffer.cpp
@@ -0,0 +1,56 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/tests.h"
+
+#include "td/utils/buffer.h"
+#include "td/utils/Random.h"
+
+TEST(Buffer, buffer_builder) {
+ {
+ td::BufferBuilder builder;
+ builder.append("b");
+ builder.prepend("a");
+ builder.append("c");
+ ASSERT_EQ(builder.extract().as_slice(), "abc");
+ }
+ {
+ td::BufferBuilder builder{"hello", 0, 0};
+ ASSERT_EQ(builder.extract().as_slice(), "hello");
+ }
+ {
+ td::BufferBuilder builder{"hello", 1, 1};
+ builder.prepend("A ");
+ builder.append(" B");
+ ASSERT_EQ(builder.extract().as_slice(), "A hello B");
+ }
+ {
+ auto str = td::rand_string('a', 'z', 10000);
+ auto split_str = td::rand_split(str);
+
+ int l = td::Random::fast(0, static_cast<int>(split_str.size() - 1));
+ int r = l;
+ td::BufferBuilder builder(split_str[l], 123, 1000);
+ while (l != 0 || r != static_cast<int>(split_str.size()) - 1) {
+ if (l == 0 || (td::Random::fast_bool() && r != static_cast<int>(split_str.size() - 1))) {
+ r++;
+ if (td::Random::fast_bool()) {
+ builder.append(split_str[r]);
+ } else {
+ builder.append(td::BufferSlice(split_str[r]));
+ }
+ } else {
+ l--;
+ if (td::Random::fast_bool()) {
+ builder.prepend(split_str[l]);
+ } else {
+ builder.prepend(td::BufferSlice(split_str[l]));
+ }
+ }
+ }
+ ASSERT_EQ(builder.extract().as_slice(), str);
+ }
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp b/protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp
index faf4ef61a4..9e81ef132c 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/crypto.cpp
@@ -1,20 +1,47 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/base64.h"
+#include "td/utils/benchmark.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
+#include "td/utils/Random.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
#include "td/utils/tests.h"
+#include "td/utils/UInt.h"
#include <limits>
static td::vector<td::string> strings{"", "1", "short test string", td::string(1000000, 'a')};
#if TD_HAVE_OPENSSL
+#if TD_HAVE_ZLIB
+TEST(Crypto, Aes) {
+ td::Random::Xorshift128plus rnd(123);
+ td::UInt256 key;
+ rnd.bytes(as_slice(key));
+ td::string plaintext(16, '\0');
+ td::string encrypted(16, '\0');
+ td::string decrypted(16, '\0');
+ rnd.bytes(plaintext);
+
+ td::AesState encryptor;
+ encryptor.init(as_slice(key), true);
+ td::AesState decryptor;
+ decryptor.init(as_slice(key), false);
+
+ encryptor.encrypt(td::as_slice(plaintext).ubegin(), td::as_slice(encrypted).ubegin(), 16);
+ decryptor.decrypt(td::as_slice(encrypted).ubegin(), td::as_slice(decrypted).ubegin(), 16);
+
+ CHECK(decrypted == plaintext);
+ CHECK(decrypted != encrypted);
+ CHECK(td::crc32(encrypted) == 178892237);
+}
+
TEST(Crypto, AesCtrState) {
td::vector<td::uint32> answers1{0u, 1141589763u, 596296607u, 3673001485u, 2302125528u,
330967191u, 2047392231u, 3537459563u, 307747798u, 2149598133u};
@@ -42,46 +69,177 @@ TEST(Crypto, AesCtrState) {
}
td::AesCtrState state;
- state.init(key, iv);
+ state.init(as_slice(key), as_slice(iv));
td::string t(length, '\0');
- state.encrypt(s, t);
+ std::size_t pos = 0;
+ for (const auto &str : td::rand_split(td::string(length, '\0'))) {
+ auto len = str.size();
+ state.encrypt(td::Slice(s).substr(pos, len), td::MutableSlice(t).substr(pos, len));
+ pos += len;
+ }
ASSERT_EQ(answers1[i], td::crc32(t));
- state.init(key, iv);
- state.decrypt(t, t);
- ASSERT_STREQ(s, t);
+ state.init(as_slice(key), as_slice(iv));
+ pos = 0;
+ for (const auto &str : td::rand_split(td::string(length, '\0'))) {
+ auto len = str.size();
+ state.decrypt(td::Slice(t).substr(pos, len), td::MutableSlice(t).substr(pos, len));
+ pos += len;
+ }
+ ASSERT_STREQ(td::base64_encode(s), td::base64_encode(t));
for (auto &c : iv.raw) {
c = 0xFF;
}
- state.init(key, iv);
- state.encrypt(s, t);
+ state.init(as_slice(key), as_slice(iv));
+ pos = 0;
+ for (const auto &str : td::rand_split(td::string(length, '\0'))) {
+ auto len = str.size();
+ state.encrypt(td::Slice(s).substr(pos, len), td::MutableSlice(t).substr(pos, len));
+ pos += len;
+ }
ASSERT_EQ(answers2[i], td::crc32(t));
i++;
}
}
+TEST(Crypto, AesIgeState) {
+ td::vector<td::uint32> answers1{0u, 2045698207u, 2423540300u, 525522475u, 1545267325u, 724143417u};
+
+ std::size_t i = 0;
+ for (auto length : {0, 16, 32, 256, 1024, 65536}) {
+ td::uint32 seed = length;
+ td::string s(length, '\0');
+ for (auto &c : s) {
+ seed = seed * 123457567u + 987651241u;
+ c = static_cast<char>((seed >> 23) & 255);
+ }
+
+ td::UInt256 key;
+ for (auto &c : key.raw) {
+ seed = seed * 123457567u + 987651241u;
+ c = (seed >> 23) & 255;
+ }
+ td::UInt256 iv;
+ for (auto &c : iv.raw) {
+ seed = seed * 123457567u + 987651241u;
+ c = (seed >> 23) & 255;
+ }
+
+ td::AesIgeState state;
+ state.init(as_slice(key), as_slice(iv), true);
+ td::string t(length, '\0');
+ td::UInt256 iv_copy = iv;
+ td::string u(length, '\0');
+ std::size_t pos = 0;
+ for (const auto &str : td::rand_split(td::string(length / 16, '\0'))) {
+ auto len = 16 * str.size();
+ state.encrypt(td::Slice(s).substr(pos, len), td::MutableSlice(t).substr(pos, len));
+ td::aes_ige_encrypt(as_slice(key), as_slice(iv_copy), td::Slice(s).substr(pos, len),
+ td::MutableSlice(u).substr(pos, len));
+ pos += len;
+ }
+
+ ASSERT_EQ(answers1[i], td::crc32(t));
+ ASSERT_EQ(answers1[i], td::crc32(u));
+
+ state.init(as_slice(key), as_slice(iv), false);
+ iv_copy = iv;
+ pos = 0;
+ for (const auto &str : td::rand_split(td::string(length / 16, '\0'))) {
+ auto len = 16 * str.size();
+ state.decrypt(td::Slice(t).substr(pos, len), td::MutableSlice(t).substr(pos, len));
+ td::aes_ige_decrypt(as_slice(key), as_slice(iv_copy), td::Slice(u).substr(pos, len),
+ td::MutableSlice(u).substr(pos, len));
+ pos += len;
+ }
+ ASSERT_STREQ(td::base64_encode(s), td::base64_encode(t));
+ ASSERT_STREQ(td::base64_encode(s), td::base64_encode(u));
+
+ i++;
+ }
+}
+
+TEST(Crypto, AesCbcState) {
+ td::vector<td::uint32> answers1{0u, 3617355989u, 3449188102u, 186999968u, 4244808847u, 2626031206u};
+
+ std::size_t i = 0;
+ for (auto length : {0, 16, 32, 256, 1024, 65536}) {
+ td::uint32 seed = length;
+ td::string s(length, '\0');
+ for (auto &c : s) {
+ seed = seed * 123457567u + 987651241u;
+ c = static_cast<char>((seed >> 23) & 255);
+ }
+
+ td::UInt256 key;
+ for (auto &c : key.raw) {
+ seed = seed * 123457567u + 987651241u;
+ c = (seed >> 23) & 255;
+ }
+ td::UInt128 iv;
+ for (auto &c : iv.raw) {
+ seed = seed * 123457567u + 987651241u;
+ c = (seed >> 23) & 255;
+ }
+
+ td::AesCbcState state(as_slice(key), as_slice(iv));
+ td::string t(length, '\0');
+ td::UInt128 iv_copy = iv;
+ td::string u(length, '\0');
+ std::size_t pos = 0;
+ for (const auto &str : td::rand_split(td::string(length / 16, '\0'))) {
+ auto len = 16 * str.size();
+ state.encrypt(td::Slice(s).substr(pos, len), td::MutableSlice(t).substr(pos, len));
+ td::aes_cbc_encrypt(as_slice(key), as_slice(iv_copy), td::Slice(s).substr(pos, len),
+ td::MutableSlice(u).substr(pos, len));
+ pos += len;
+ }
+
+ ASSERT_EQ(answers1[i], td::crc32(t));
+ ASSERT_EQ(answers1[i], td::crc32(u));
+
+ state = td::AesCbcState(as_slice(key), as_slice(iv));
+ iv_copy = iv;
+ pos = 0;
+ for (const auto &str : td::rand_split(td::string(length / 16, '\0'))) {
+ auto len = 16 * str.size();
+ state.decrypt(td::Slice(t).substr(pos, len), td::MutableSlice(t).substr(pos, len));
+ td::aes_cbc_decrypt(as_slice(key), as_slice(iv_copy), td::Slice(u).substr(pos, len),
+ td::MutableSlice(u).substr(pos, len));
+ pos += len;
+ }
+ ASSERT_STREQ(td::base64_encode(s), td::base64_encode(t));
+ ASSERT_STREQ(td::base64_encode(s), td::base64_encode(u));
+
+ i++;
+ }
+}
+#endif
+
TEST(Crypto, Sha256State) {
for (auto length : {0, 1, 31, 32, 33, 9999, 10000, 10001, 999999, 1000001}) {
auto s = td::rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), length);
td::UInt256 baseline;
- td::sha256(s, td::MutableSlice(baseline.raw, 32));
+ td::sha256(s, as_slice(baseline));
td::Sha256State state;
- td::sha256_init(&state);
+ state.init();
+ td::Sha256State state2 = std::move(state);
auto v = td::rand_split(s);
for (auto &x : v) {
- td::sha256_update(x, &state);
+ state2.feed(x);
}
+ state = std::move(state2);
td::UInt256 result;
- td::sha256_final(&state, td::MutableSlice(result.raw, 32));
+ state.extract(as_slice(result));
ASSERT_TRUE(baseline == result);
}
}
TEST(Crypto, PBKDF) {
- td::vector<td::string> passwords{"", "qwerty", std::string(1000, 'a')};
- td::vector<td::string> salts{"", "qwerty", std::string(1000, 'a')};
+ td::vector<td::string> passwords{"", "qwerty", td::string(1000, 'a')};
+ td::vector<td::string> salts{"", "qwerty", td::string(1000, 'a')};
td::vector<int> iteration_counts{1, 2, 1000};
td::vector<td::Slice> answers{
"984LZT0tcqQQjPWr6RL/3Xd2Ftu7J6cOggTzri0Pb60=", "lzmEEdaupDp3rO+SImq4J41NsGaL0denanJfdoCsRcU=",
@@ -145,6 +303,32 @@ TEST(Crypto, md5) {
ASSERT_STREQ(answers[i], td::base64_encode(output));
}
}
+
+TEST(Crypto, hmac_sha256) {
+ td::vector<td::Slice> answers{
+ "t33rfT85UOe6N00BhsNwobE+f2TnW331HhdvQ4GdJp8=", "BQl5HF2jqhCz4JTqhAs+H364oxboh7QlluOMHuuRVh8=",
+ "NCCPuZBsAPBd/qr3SyeYE+e1RNgzkKJCS/+eXDBw8zU=", "mo3ahTkyLKfoQoYA0s7vRZULuH++vqwFJD0U5n9HHw0="};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ td::string output(32, '\0');
+ td::hmac_sha256("cucumber", strings[i], output);
+ ASSERT_STREQ(answers[i], td::base64_encode(output));
+ }
+}
+
+TEST(Crypto, hmac_sha512) {
+ td::vector<td::Slice> answers{
+ "o28hTN1m/TGlm/VYxDIzOdUE4wMpQzO8hVcTkiP2ezEJXtrOvCjRnl20aOV1S8axA5Te0TzIjfIoEAtpzamIsA==",
+ "32X3GslSz0HDznSrCNt++ePRcFVSUSD+tfOVannyxS+yLt/om11qILCE64RFTS8/B84gByMzC3FuAlfcIam/KA==",
+ "BVqe5rK1Fg1i+C7xXTAzT9vDPcf3kQQpTtse6rT/EVDzKo9AUo4ZwyUyJ0KcLHoffIjul/TuJoBg+wLz7Z7r7g==",
+ "WASmeku5Pcfz7N0Kp4Q3I9sxtO2MiaBXA418CY0HvjdtmAo7QY+K3E0o9UemgGzz41KqeypzRC92MwOAOnXJLA=="};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ td::string output(64, '\0');
+ td::hmac_sha512("cucumber", strings[i], output);
+ ASSERT_STREQ(answers[i], td::base64_encode(output));
+ }
+}
#endif
#if TD_HAVE_ZLIB
@@ -157,6 +341,69 @@ TEST(Crypto, crc32) {
}
#endif
+#if TD_HAVE_CRC32C
+TEST(Crypto, crc32c) {
+ td::vector<td::uint32> answers{0u, 2432014819u, 1077264849u, 1131405888u};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ ASSERT_EQ(answers[i], td::crc32c(strings[i]));
+
+ auto v = td::rand_split(strings[i]);
+ td::uint32 a = 0;
+ td::uint32 b = 0;
+ for (auto &x : v) {
+ a = td::crc32c_extend(a, x);
+ auto x_crc = td::crc32c(x);
+ b = td::crc32c_extend(b, x_crc, x.size());
+ }
+ ASSERT_EQ(answers[i], a);
+ ASSERT_EQ(answers[i], b);
+ }
+}
+
+TEST(Crypto, crc32c_benchmark) {
+ class Crc32cExtendBenchmark final : public td::Benchmark {
+ public:
+ explicit Crc32cExtendBenchmark(size_t chunk_size) : chunk_size_(chunk_size) {
+ }
+ td::string get_description() const final {
+ return PSTRING() << "Crc32c with chunk_size=" << chunk_size_;
+ }
+ void start_up_n(int n) final {
+ if (n > (1 << 20)) {
+ cnt_ = n / (1 << 20);
+ n = (1 << 20);
+ } else {
+ cnt_ = 1;
+ }
+ data_ = td::string(n, 'a');
+ }
+ void run(int n) final {
+ td::uint32 res = 0;
+ for (int i = 0; i < cnt_; i++) {
+ td::Slice data(data_);
+ while (!data.empty()) {
+ auto head = data.substr(0, chunk_size_);
+ data = data.substr(head.size());
+ res = td::crc32c_extend(res, head);
+ }
+ }
+ td::do_not_optimize_away(res);
+ }
+
+ private:
+ size_t chunk_size_;
+ td::string data_;
+ int cnt_;
+ };
+ bench(Crc32cExtendBenchmark(2));
+ bench(Crc32cExtendBenchmark(8));
+ bench(Crc32cExtendBenchmark(32));
+ bench(Crc32cExtendBenchmark(128));
+ bench(Crc32cExtendBenchmark(65536));
+}
+#endif
+
TEST(Crypto, crc64) {
td::vector<td::uint64> answers{0ull, 3039664240384658157ull, 17549519902062861804ull, 8794730974279819706ull};
@@ -164,3 +411,61 @@ TEST(Crypto, crc64) {
ASSERT_EQ(answers[i], td::crc64(strings[i]));
}
}
+
+TEST(Crypto, crc16) {
+ td::vector<td::uint16> answers{0, 9842, 25046, 37023};
+
+ for (std::size_t i = 0; i < strings.size(); i++) {
+ ASSERT_EQ(answers[i], td::crc16(strings[i]));
+ }
+}
+
+static td::Slice rsa_private_key = R"ABCD(
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeYT5/prmLEa2Q
+tZND+UwTmif8kl2VlXaMCjj1k1lJJq8BqS8cVM2vPnOPzFoiC2LYykhm4kk7goCC
+ZH6wez9yakg28fcq0Ycv0x8DL1K+VKHJuwIhVfQs//IY1/cBOrMESc+NQowPbv1t
+TIFxBO2gebnpLuseht8ix7XtpGC4qAaHN2aEvT2cRsnA76TAK1RVxf1OYGUFBDzY
+318WpVZfVIjcQ7K9+eU6b2Yb84VLlvJXw3e1rvw+fBzx2EjpD4zhXy11YppWDyV6
+HEb2hs3cGS/LbHfHvdcSfil2omaJP97MDEEY2HFxjR/E5CEf2suvPzX4XS3RE+S3
+2aEJaaQbAgMBAAECggEAKo3XRNwls0wNt5xXcvF4smOUdUuY5u/0AHZQUgYBVvM1
+GA9E+ZnsxjUgLgs/0DX3k16aHj39H4sohksuxxy+lmlqKkGBN8tioC85RwW+Qre1
+QgIsNS7ai+XqcQCavrx51z88nV53qNhnXIwAVR1JT6Ubg1i8G1pZxrEKyk/jRlJd
+mGjf6vjitH//PPkghPJ/D42k93YRcy+duOgqYDQpLZp8DiEGfYrX10B1H7HrWLV+
+Wp5KO1YXtKgQUplj6kYy72bVajbxYTvzgjaaKsh74jBO0uT3tHTtXG0dcKGb0VR/
+cqP/1H/lC9bAnAqAGefNusGJQZIElvTsrpIQXOeZsQKBgQD2W04S+FjqYYFjnEFX
+6eL4it01afs5M3/C6CcI5JQtN6p+Na4NCSILol33xwhakn87zqdADHawBYQVQ8Uw
+dPurl805wfkzN3AbfdDmtx0IJ8vK4HFpktRjfpwBVhlVtm1doAYFqqsuCF2vWW1t
+mM2YOSq4AnRHCeBb/P6kRIW0MwKBgQDnFawKKqiC4tuyBOkkEhexlm7x9he0md7D
+3Z2hc3Bmdcq1niw4wBq3HUxGLReGCcSr5epKSQwkunlTn5ZSC6Rmbe4zxsGIwbb3
+5W3342swBaoxEIuBokBvZ/xUOXVwiqKj+S/NzVkZcnT6K9V/HnUCQR+JBbQxFQaX
+iiezcjKoeQKBgCIVUcDoIQ0UPl10ocmy7xbpx177calhSZzCl5vwW9vBptHdRV5C
+VDZ92ThNjgdR205/8b23u7fwm2yBusdQd/0ufFMwVfTTB6yWBI/W56pYLya7VJWB
+nebB/n1k1w53tbvNRugDy7kLqUJ4Qd521ILp7dIVbNbjM+omH2jEnibnAoGBAIM5
+a1jaoJay/M86uqohHBNcuePtO8jzF+1iDAGC7HFCsrov+CzB6mnR2V6AfLtBEM4M
+4d8NXDf/LKawGUy+D72a74m3dG+UkbJ0Nt5t5pB+pwb1vkL/QFgDVOb/OhGOqI01
+FFBqLA6nUIZAHhzxzsBY+u90rb6xkey8J49faiUBAoGAaMgOgEvQB5H19ZL5tMkl
+A/DKtTz/NFzN4Zw/vNPVb7eNn4jg9M25d9xqvL4acOa+nuV3nLHbcUWE1/7STXw1
+gT58CvoEmD1AiP95nup+HKHENJ1DWMgF5MDfVQwGCvWP5/Qy89ybr0eG8HjbldbN
+MpSmzz2wOz152oGdOd3syT4=
+-----END PRIVATE KEY-----
+)ABCD";
+
+static td::Slice rsa_public_key = R"ABCD(
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3mE+f6a5ixGtkLWTQ/lM
+E5on/JJdlZV2jAo49ZNZSSavAakvHFTNrz5zj8xaIgti2MpIZuJJO4KAgmR+sHs/
+cmpINvH3KtGHL9MfAy9SvlShybsCIVX0LP/yGNf3ATqzBEnPjUKMD279bUyBcQTt
+oHm56S7rHobfIse17aRguKgGhzdmhL09nEbJwO+kwCtUVcX9TmBlBQQ82N9fFqVW
+X1SI3EOyvfnlOm9mG/OFS5byV8N3ta78Pnwc8dhI6Q+M4V8tdWKaVg8lehxG9obN
+3Bkvy2x3x73XEn4pdqJmiT/ezAxBGNhxcY0fxOQhH9rLrz81+F0t0RPkt9mhCWmk
+GwIDAQAB
+-----END PUBLIC KEY-----
+)ABCD";
+
+TEST(Crypto, rsa) {
+ auto value = td::rand_string('a', 'z', 200);
+ auto encrypted_value = td::rsa_encrypt_pkcs1_oaep(rsa_public_key, value).move_as_ok();
+ auto decrypted_value = td::rsa_decrypt_pkcs1_oaep(rsa_private_key, encrypted_value.as_slice()).move_as_ok();
+ ASSERT_TRUE(decrypted_value.as_slice().truncate(value.size()) == value);
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/emoji.cpp b/protocols/Telegram/tdlib/td/tdutils/test/emoji.cpp
new file mode 100644
index 0000000000..c8e6539a99
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/emoji.cpp
@@ -0,0 +1,128 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/emoji.h"
+#include "td/utils/tests.h"
+
+TEST(Emoji, is_emoji) {
+ ASSERT_TRUE(!td::is_emoji(""));
+ ASSERT_TRUE(td::is_emoji("👩🏼‍❤‍💋‍👩🏻"));
+ ASSERT_TRUE(td::is_emoji("👩🏼‍❤️‍💋‍👩🏻"));
+ ASSERT_TRUE(!td::is_emoji("👩🏼‍❤️️‍💋‍👩🏻"));
+ ASSERT_TRUE(td::is_emoji("⌚"));
+ ASSERT_TRUE(td::is_emoji("↔"));
+ ASSERT_TRUE(td::is_emoji("🪗"));
+ ASSERT_TRUE(td::is_emoji("2️⃣"));
+ ASSERT_TRUE(td::is_emoji("2⃣"));
+ ASSERT_TRUE(!td::is_emoji(" 2⃣"));
+ ASSERT_TRUE(!td::is_emoji("2⃣ "));
+ ASSERT_TRUE(!td::is_emoji(" "));
+ ASSERT_TRUE(!td::is_emoji(""));
+ ASSERT_TRUE(!td::is_emoji("1234567890123456789012345678901234567890123456789012345678901234567890"));
+ ASSERT_TRUE(td::is_emoji("❤️"));
+ ASSERT_TRUE(td::is_emoji("❤"));
+ ASSERT_TRUE(td::is_emoji("⌚"));
+ ASSERT_TRUE(td::is_emoji("🎄"));
+ ASSERT_TRUE(td::is_emoji("🧑‍🎄"));
+}
+
+static void test_get_fitzpatrick_modifier(td::string emoji, int result) {
+ ASSERT_EQ(result, td::get_fitzpatrick_modifier(emoji));
+}
+
+TEST(Emoji, get_fitzpatrick_modifier) {
+ test_get_fitzpatrick_modifier("", 0);
+ test_get_fitzpatrick_modifier("👩🏼‍❤‍💋‍👩🏻", 2);
+ test_get_fitzpatrick_modifier("👩🏼‍❤️‍💋‍👩🏻", 2);
+ test_get_fitzpatrick_modifier("👋", 0);
+ test_get_fitzpatrick_modifier("👋🏻", 2);
+ test_get_fitzpatrick_modifier("👋🏼", 3);
+ test_get_fitzpatrick_modifier("👋🏽", 4);
+ test_get_fitzpatrick_modifier("👋🏾", 5);
+ test_get_fitzpatrick_modifier("👋🏿", 6);
+ test_get_fitzpatrick_modifier("🏻", 2);
+ test_get_fitzpatrick_modifier("🏼", 3);
+ test_get_fitzpatrick_modifier("🏽", 4);
+ test_get_fitzpatrick_modifier("🏾", 5);
+ test_get_fitzpatrick_modifier("🏿", 6);
+ test_get_fitzpatrick_modifier("⌚", 0);
+ test_get_fitzpatrick_modifier("↔", 0);
+ test_get_fitzpatrick_modifier("🪗", 0);
+ test_get_fitzpatrick_modifier("2️⃣", 0);
+ test_get_fitzpatrick_modifier("2⃣", 0);
+ test_get_fitzpatrick_modifier("❤️", 0);
+ test_get_fitzpatrick_modifier("❤", 0);
+ test_get_fitzpatrick_modifier("⌚", 0);
+ test_get_fitzpatrick_modifier("🎄", 0);
+ test_get_fitzpatrick_modifier("🧑‍🎄", 0);
+}
+
+static void test_remove_emoji_modifiers(td::string emoji, const td::string &result) {
+ ASSERT_STREQ(result, td::remove_emoji_modifiers(emoji));
+ td::remove_emoji_modifiers_in_place(emoji);
+ ASSERT_STREQ(result, emoji);
+ ASSERT_STREQ(emoji, td::remove_emoji_modifiers(emoji));
+}
+
+TEST(Emoji, remove_emoji_modifiers) {
+ test_remove_emoji_modifiers("", "");
+ test_remove_emoji_modifiers("👩🏼‍❤‍💋‍👩🏻", "👩‍❤‍💋‍👩");
+ test_remove_emoji_modifiers("👩🏼‍❤️‍💋‍👩🏻", "👩‍❤‍💋‍👩");
+ test_remove_emoji_modifiers("👋🏻", "👋");
+ test_remove_emoji_modifiers("👋🏼", "👋");
+ test_remove_emoji_modifiers("👋🏽", "👋");
+ test_remove_emoji_modifiers("👋🏾", "👋");
+ test_remove_emoji_modifiers("👋🏿", "👋");
+ test_remove_emoji_modifiers("🏻", "🏻");
+ test_remove_emoji_modifiers("🏼", "🏼");
+ test_remove_emoji_modifiers("🏽", "🏽");
+ test_remove_emoji_modifiers("🏾", "🏾");
+ test_remove_emoji_modifiers("🏿", "🏿");
+ test_remove_emoji_modifiers("⌚", "⌚");
+ test_remove_emoji_modifiers("↔", "↔");
+ test_remove_emoji_modifiers("🪗", "🪗");
+ test_remove_emoji_modifiers("2️⃣", "2⃣");
+ test_remove_emoji_modifiers("2⃣", "2⃣");
+ test_remove_emoji_modifiers("❤️", "❤");
+ test_remove_emoji_modifiers("❤", "❤");
+ test_remove_emoji_modifiers("⌚", "⌚");
+ test_remove_emoji_modifiers("️", "️");
+ test_remove_emoji_modifiers("️️️🏻", "️️️🏻");
+ test_remove_emoji_modifiers("️️️🏻a", "a");
+ test_remove_emoji_modifiers("🎄", "🎄");
+ test_remove_emoji_modifiers("🧑‍🎄", "🧑‍🎄");
+}
+
+static void test_remove_emoji_selectors(td::string emoji, const td::string &result) {
+ ASSERT_STREQ(result, td::remove_emoji_selectors(result));
+ ASSERT_STREQ(result, td::remove_emoji_selectors(emoji));
+}
+
+TEST(Emoji, remove_emoji_selectors) {
+ test_remove_emoji_selectors("", "");
+ test_remove_emoji_selectors("👩🏼‍❤‍💋‍👩🏻", "👩🏼‍❤‍💋‍👩🏻");
+ test_remove_emoji_selectors("👩🏼‍❤️‍💋‍👩🏻", "👩🏼‍❤‍💋‍👩🏻");
+ test_remove_emoji_selectors("👋🏻", "👋🏻");
+ test_remove_emoji_selectors("👋🏼", "👋🏼");
+ test_remove_emoji_selectors("👋🏽", "👋🏽");
+ test_remove_emoji_selectors("👋🏾", "👋🏾");
+ test_remove_emoji_selectors("👋🏿", "👋🏿");
+ test_remove_emoji_selectors("🏻", "🏻");
+ test_remove_emoji_selectors("🏼", "🏼");
+ test_remove_emoji_selectors("🏽", "🏽");
+ test_remove_emoji_selectors("🏾", "🏾");
+ test_remove_emoji_selectors("🏿", "🏿");
+ test_remove_emoji_selectors("⌚", "⌚");
+ test_remove_emoji_selectors("↔", "↔");
+ test_remove_emoji_selectors("🪗", "🪗");
+ test_remove_emoji_selectors("2️⃣", "2⃣");
+ test_remove_emoji_selectors("2⃣", "2⃣");
+ test_remove_emoji_selectors("❤️", "❤");
+ test_remove_emoji_selectors("❤", "❤");
+ test_remove_emoji_selectors("⌚", "⌚");
+ test_remove_emoji_selectors("🎄", "🎄");
+ test_remove_emoji_selectors("🧑‍🎄", "🧑‍🎄");
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp b/protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp
index a0a92c14eb..de2def1e5e 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/filesystem.cpp
@@ -1,41 +1,47 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/filesystem.h"
+#include "td/utils/Slice.h"
#include "td/utils/tests.h"
+static void test_clean_filename(td::CSlice name, td::Slice result) {
+ ASSERT_STREQ(td::clean_filename(name), result);
+}
+
TEST(Misc, clean_filename) {
- using td::clean_filename;
- ASSERT_STREQ(clean_filename("-1234567"), "-1234567");
- ASSERT_STREQ(clean_filename(".git"), "git");
- ASSERT_STREQ(clean_filename("../../.git"), "git");
- ASSERT_STREQ(clean_filename(".././.."), "");
- ASSERT_STREQ(clean_filename("../"), "");
- ASSERT_STREQ(clean_filename(".."), "");
- ASSERT_STREQ(clean_filename("test/git/ as dsa . a"), "as dsa.a");
- ASSERT_STREQ(clean_filename(" . "), "");
- ASSERT_STREQ(clean_filename("!@#$%^&*()_+-=[]{;|:\"}'<>?,.`~"), "!@#$%^ ()_+-=[]{; } ,.~");
- ASSERT_STREQ(clean_filename("!@#$%^&*()_+-=[]{}\\|:\";'<>?,.`~"), "; ,.~");
- ASSERT_STREQ(clean_filename("عرفها بعد قد. هذا مع تاريخ اليميني واندونيسيا،, لعدم تاريخ لهيمنة الى"),
- "عرفها بعد قد.هذا مع تاريخ اليميني");
- ASSERT_STREQ(
- clean_filename(
- "012345678901234567890123456789012345678901234567890123456789adsasdasdsaa.01234567890123456789asdasdasdasd"),
- "012345678901234567890123456789012345678901234567890123456789.01234567890123456789");
- ASSERT_STREQ(clean_filename("01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. "
- "0123456789`<><<>><><>0123456789asdasdasdasd"),
- "01234567890123456789012345678901234567890123456789.0123456789");
- ASSERT_STREQ(clean_filename("01234567890123456789012345678901234567890123456789<>*?: <>*?:0123456789adsasdasdsaa. "
- "0123456789`<><><>0123456789asdasdasdasd"),
- "01234567890123456789012345678901234567890123456789.0123456789 012");
- ASSERT_STREQ(clean_filename("C:/document.tar.gz"), "document.tar.gz");
- ASSERT_STREQ(clean_filename("test...."), "test");
- ASSERT_STREQ(clean_filename("....test"), "test");
- ASSERT_STREQ(clean_filename("test.exe...."), "test.exe"); // extension has changed
- ASSERT_STREQ(clean_filename("test.exe01234567890123456789...."),
- "test.exe01234567890123456789"); // extension may be more then 20 characters
- ASSERT_STREQ(clean_filename("....test....asdf"), "test.asdf");
+ test_clean_filename("-1234567", "-1234567");
+ test_clean_filename(".git", "git");
+ test_clean_filename("../../.git", "git");
+ test_clean_filename(".././..", "");
+ test_clean_filename("../", "");
+ test_clean_filename("..", "");
+ test_clean_filename("test/git/ as dsa . a", "as dsa.a");
+ test_clean_filename(" . ", "");
+ test_clean_filename("!@#$%^&*()_+-=[]{;|:\"}'<>?,.`~", "!@#$%^ ()_+-=[]{; } ,.~");
+ test_clean_filename("!@#$%^&*()_+-=[]{}\\|:\";'<>?,.`~", "; ,.~");
+ test_clean_filename("عرفها بعد قد. هذا مع تاريخ اليميني واندونيسيا،, لعدم تاريخ لهيمنة الى",
+ "عرفها بعد قد.هذا مع تاريخ الي");
+ test_clean_filename(
+ "012345678901234567890123456789012345678901234567890123456789adsasdasdsaa.01234567890123456789asdasdasdasd",
+ "012345678901234567890123456789012345678901234567890123456789adsa.0123456789012345");
+ test_clean_filename(
+ "01234567890123456789012345678901234567890123456789adsa<>*?: <>*?:0123456789adsasdasdsaa. "
+ "0123456789`<><<>><><>0123456789asdasdasdasd",
+ "01234567890123456789012345678901234567890123456789adsa.0123456789");
+ test_clean_filename(
+ "012345678901234567890123456789012345678901234567890123<>*?: <>*?:0123456789adsasdasdsaa. "
+ "0123456789`<>0123456789asdasdasdasd",
+ "012345678901234567890123456789012345678901234567890123.0123456789 012");
+ test_clean_filename("C:/document.tar.gz", "document.tar.gz");
+ test_clean_filename("test....", "test");
+ test_clean_filename("....test", "test");
+ test_clean_filename("test.exe....", "test.exe"); // extension has changed
+ test_clean_filename("test.exe01234567890123456789....",
+ "test.exe01234567890123456789"); // extension may be more than 16 characters
+ test_clean_filename("....test....asdf", "test.asdf");
+ test_clean_filename("കറുപ്പ്.txt", "കറപപ.txt");
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp b/protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp
index e4bd81eb0d..32d75474e8 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/gzip.cpp
@@ -1,49 +1,59 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
+#include "td/utils/algorithm.h"
#include "td/utils/buffer.h"
#include "td/utils/ByteFlow.h"
+#include "td/utils/common.h"
#include "td/utils/Gzip.h"
#include "td/utils/GzipByteFlow.h"
#include "td/utils/logging.h"
+#include "td/utils/port/thread_local.h"
+#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/tests.h"
+#include "td/utils/Time.h"
-static void encode_decode(td::string s) {
+static void encode_decode(const td::string &s) {
auto r = td::gzencode(s, 2);
ASSERT_TRUE(!r.empty());
- if (r.empty()) {
- return;
- }
- auto new_s = td::gzdecode(r.as_slice());
- ASSERT_TRUE(!new_s.empty());
- if (new_s.empty()) {
- return;
- }
- ASSERT_EQ(s, new_s.as_slice().str());
+ ASSERT_EQ(s, td::gzdecode(r.as_slice()));
}
TEST(Gzip, gzencode_gzdecode) {
- auto str = td::rand_string(0, 127, 1000);
- encode_decode(str);
- str = td::rand_string('a', 'z', 1000000);
- encode_decode(str);
- str = td::string(1000000, 'a');
- encode_decode(str);
+ encode_decode(td::rand_string(0, 255, 1000));
+ encode_decode(td::rand_string('a', 'z', 1000000));
+ encode_decode(td::string(1000000, 'a'));
+}
+
+static void test_gzencode(const td::string &s) {
+ auto begin_time = td::Time::now();
+ auto r = td::gzencode(s, td::max(2, static_cast<int>(100 / s.size())));
+ ASSERT_TRUE(!r.empty());
+ LOG(INFO) << "Encoded string of size " << s.size() << " in " << (td::Time::now() - begin_time)
+ << " with compression ratio " << static_cast<double>(r.size()) / static_cast<double>(s.size());
+}
+
+TEST(Gzip, gzencode) {
+ for (size_t len = 1; len <= 10000000; len *= 10) {
+ test_gzencode(td::rand_string('a', 'a', len));
+ test_gzencode(td::rand_string('a', 'z', len));
+ test_gzencode(td::rand_string(0, 255, len));
+ }
}
TEST(Gzip, flow) {
auto str = td::rand_string('a', 'z', 1000000);
auto parts = td::rand_split(str);
- auto input_writer = td::ChainBufferWriter::create_empty();
+ td::ChainBufferWriter input_writer;
auto input = input_writer.extract_reader();
td::ByteFlowSource source(&input);
- td::GzipByteFlow gzip_flow(td::Gzip::Encode);
- gzip_flow = td::GzipByteFlow(td::Gzip::Encode);
+ td::GzipByteFlow gzip_flow(td::Gzip::Mode::Encode);
+ gzip_flow = td::GzipByteFlow(td::Gzip::Mode::Encode);
td::ByteFlowSink sink;
source >> gzip_flow >> sink;
@@ -63,14 +73,15 @@ TEST(Gzip, flow) {
}
TEST(Gzip, flow_error) {
auto str = td::rand_string('a', 'z', 1000000);
- auto zip = td::gzencode(str).as_slice().str();
+ auto zip = td::gzencode(str, 0.9).as_slice().str();
+ ASSERT_TRUE(!zip.empty());
zip.resize(zip.size() - 1);
auto parts = td::rand_split(zip);
auto input_writer = td::ChainBufferWriter();
auto input = input_writer.extract_reader();
td::ByteFlowSource source(&input);
- td::GzipByteFlow gzip_flow(td::Gzip::Decode);
+ td::GzipByteFlow gzip_flow(td::Gzip::Mode::Decode);
td::ByteFlowSink sink;
source >> gzip_flow >> sink;
@@ -89,13 +100,13 @@ TEST(Gzip, flow_error) {
TEST(Gzip, encode_decode_flow) {
auto str = td::rand_string('a', 'z', 1000000);
auto parts = td::rand_split(str);
- auto input_writer = td::ChainBufferWriter::create_empty();
+ td::ChainBufferWriter input_writer;
auto input = input_writer.extract_reader();
td::ByteFlowSource source(&input);
- td::GzipByteFlow gzip_encode_flow(td::Gzip::Encode);
- td::GzipByteFlow gzip_decode_flow(td::Gzip::Decode);
- td::GzipByteFlow gzip_encode_flow2(td::Gzip::Encode);
- td::GzipByteFlow gzip_decode_flow2(td::Gzip::Decode);
+ td::GzipByteFlow gzip_encode_flow(td::Gzip::Mode::Encode);
+ td::GzipByteFlow gzip_decode_flow(td::Gzip::Mode::Decode);
+ td::GzipByteFlow gzip_encode_flow2(td::Gzip::Mode::Encode);
+ td::GzipByteFlow gzip_decode_flow2(td::Gzip::Mode::Decode);
td::ByteFlowSink sink;
source >> gzip_encode_flow >> gzip_decode_flow >> gzip_encode_flow2 >> gzip_decode_flow2 >> sink;
@@ -111,3 +122,126 @@ TEST(Gzip, encode_decode_flow) {
ASSERT_TRUE(sink.status().is_ok());
ASSERT_EQ(str, sink.result()->move_as_buffer_slice().as_slice().str());
}
+
+TEST(Gzip, encode_decode_flow_big) {
+ td::clear_thread_locals();
+ auto start_mem = td::BufferAllocator::get_buffer_mem();
+ {
+ auto str = td::string(200000, 'a');
+ td::ChainBufferWriter input_writer;
+ auto input = input_writer.extract_reader();
+ td::ByteFlowSource source(&input);
+ td::GzipByteFlow gzip_encode_flow(td::Gzip::Mode::Encode);
+ td::GzipByteFlow gzip_decode_flow(td::Gzip::Mode::Decode);
+ td::GzipByteFlow gzip_encode_flow2(td::Gzip::Mode::Encode);
+ td::GzipByteFlow gzip_decode_flow2(td::Gzip::Mode::Decode);
+ td::ByteFlowSink sink;
+ source >> gzip_encode_flow >> gzip_decode_flow >> gzip_encode_flow2 >> gzip_decode_flow2 >> sink;
+
+ ASSERT_TRUE(!sink.is_ready());
+ size_t n = 200;
+ size_t left_size = n * str.size();
+ auto validate = [&](td::Slice chunk) {
+ CHECK(chunk.size() <= left_size);
+ left_size -= chunk.size();
+ ASSERT_TRUE(td::all_of(chunk, [](auto c) { return c == 'a'; }));
+ };
+
+ for (size_t i = 0; i < n; i++) {
+ input_writer.append(str);
+ source.wakeup();
+ auto extra_mem = td::BufferAllocator::get_buffer_mem() - start_mem;
+ // limit means nothing. just check that we do not use 200Mb or so
+ CHECK(extra_mem < (10 << 20));
+
+ auto size = sink.get_output()->size();
+ validate(sink.get_output()->cut_head(size).move_as_buffer_slice().as_slice());
+ }
+ ASSERT_TRUE(!sink.is_ready());
+ source.close_input(td::Status::OK());
+ ASSERT_TRUE(sink.is_ready());
+ LOG_IF(ERROR, sink.status().is_error()) << sink.status();
+ ASSERT_TRUE(sink.status().is_ok());
+ validate(sink.result()->move_as_buffer_slice().as_slice());
+ ASSERT_EQ(0u, left_size);
+ }
+ td::clear_thread_locals();
+ ASSERT_EQ(start_mem, td::BufferAllocator::get_buffer_mem());
+}
+
+TEST(Gzip, decode_encode_flow_bomb) {
+ td::string gzip_bomb_str;
+ size_t N = 200;
+ {
+ td::ChainBufferWriter input_writer;
+ auto input = input_writer.extract_reader();
+ td::GzipByteFlow gzip_flow(td::Gzip::Mode::Encode);
+ td::ByteFlowSource source(&input);
+ td::ByteFlowSink sink;
+ source >> gzip_flow >> sink;
+
+ td::string s(1 << 16, 'a');
+ for (size_t i = 0; i < N; i++) {
+ input_writer.append(s);
+ source.wakeup();
+ }
+ source.close_input(td::Status::OK());
+ ASSERT_TRUE(sink.is_ready());
+ LOG_IF(ERROR, sink.status().is_error()) << sink.status();
+ ASSERT_TRUE(sink.status().is_ok());
+ gzip_bomb_str = sink.result()->move_as_buffer_slice().as_slice().str();
+ }
+
+ td::clear_thread_locals();
+ auto start_mem = td::BufferAllocator::get_buffer_mem();
+ {
+ td::ChainBufferWriter input_writer;
+ auto input = input_writer.extract_reader();
+ td::ByteFlowSource source(&input);
+ td::GzipByteFlow::Options decode_options;
+ decode_options.write_watermark.low = 2 << 20;
+ decode_options.write_watermark.high = 4 << 20;
+ td::GzipByteFlow::Options encode_options;
+ encode_options.read_watermark.low = 2 << 20;
+ encode_options.read_watermark.high = 4 << 20;
+ td::GzipByteFlow gzip_decode_flow(td::Gzip::Mode::Decode);
+ gzip_decode_flow.set_options(decode_options);
+ td::GzipByteFlow gzip_encode_flow(td::Gzip::Mode::Encode);
+ gzip_encode_flow.set_options(encode_options);
+ td::GzipByteFlow gzip_decode_flow2(td::Gzip::Mode::Decode);
+ gzip_decode_flow2.set_options(decode_options);
+ td::GzipByteFlow gzip_encode_flow2(td::Gzip::Mode::Encode);
+ gzip_encode_flow2.set_options(encode_options);
+ td::GzipByteFlow gzip_decode_flow3(td::Gzip::Mode::Decode);
+ gzip_decode_flow3.set_options(decode_options);
+ td::ByteFlowSink sink;
+ source >> gzip_decode_flow >> gzip_encode_flow >> gzip_decode_flow2 >> gzip_encode_flow2 >> gzip_decode_flow3 >>
+ sink;
+
+ ASSERT_TRUE(!sink.is_ready());
+ size_t left_size = N * (1 << 16);
+ auto validate = [&](td::Slice chunk) {
+ CHECK(chunk.size() <= left_size);
+ left_size -= chunk.size();
+ ASSERT_TRUE(td::all_of(chunk, [](auto c) { return c == 'a'; }));
+ };
+
+ input_writer.append(gzip_bomb_str);
+ source.close_input(td::Status::OK());
+
+ do {
+ gzip_decode_flow3.wakeup();
+ gzip_decode_flow2.wakeup();
+ gzip_decode_flow.wakeup();
+ source.wakeup();
+ auto extra_mem = td::BufferAllocator::get_buffer_mem() - start_mem;
+ // limit means nothing. just check that we do not use 15Mb or so
+ CHECK(extra_mem < (5 << 20));
+ auto size = sink.get_output()->size();
+ validate(sink.get_output()->cut_head(size).move_as_buffer_slice().as_slice());
+ } while (!sink.is_ready());
+ ASSERT_EQ(0u, left_size);
+ }
+ td::clear_thread_locals();
+ ASSERT_EQ(start_mem, td::BufferAllocator::get_buffer_mem());
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/hashset_benchmark.cpp b/protocols/Telegram/tdlib/td/tdutils/test/hashset_benchmark.cpp
new file mode 100644
index 0000000000..f07f58c8f0
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/hashset_benchmark.cpp
@@ -0,0 +1,647 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/algorithm.h"
+#include "td/utils/common.h"
+#include "td/utils/FlatHashMap.h"
+#include "td/utils/FlatHashMapChunks.h"
+#include "td/utils/FlatHashTable.h"
+#include "td/utils/format.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/logging.h"
+#include "td/utils/MapNode.h"
+#include "td/utils/Random.h"
+#include "td/utils/Slice.h"
+#include "td/utils/Span.h"
+#include "td/utils/StringBuilder.h"
+#include "td/utils/Time.h"
+#include "td/utils/VectorQueue.h"
+
+#ifdef SCOPE_EXIT
+#undef SCOPE_EXIT
+#endif
+
+#include <absl/container/flat_hash_map.h>
+#include <absl/hash/hash.h>
+#include <algorithm>
+#include <benchmark/benchmark.h>
+#include <folly/container/F14Map.h>
+#include <functional>
+#include <map>
+#include <random>
+#include <unordered_map>
+#include <utility>
+
+template <class TableT>
+static void reserve(TableT &table, std::size_t size) {
+ table.reserve(size);
+}
+
+template <class A, class B>
+static void reserve(std::map<A, B> &table, std::size_t size) {
+}
+
+template <class KeyT, class ValueT>
+class NoOpTable {
+ public:
+ using key_type = KeyT;
+ using value_type = std::pair<const KeyT, ValueT>;
+ template <class It>
+ NoOpTable(It begin, It end) {
+ }
+
+ ValueT &operator[](const KeyT &) const {
+ static ValueT dummy;
+ return dummy;
+ }
+
+ KeyT find(const KeyT &key) const {
+ return key;
+ }
+};
+
+template <class KeyT, class ValueT>
+class VectorTable {
+ public:
+ using key_type = KeyT;
+ using value_type = std::pair<const KeyT, ValueT>;
+ template <class It>
+ VectorTable(It begin, It end) : table_(begin, end) {
+ }
+
+ ValueT &operator[](const KeyT &needle) {
+ auto it = find(needle);
+ if (it == table_.end()) {
+ table_.emplace_back(needle, ValueT{});
+ return table_.back().second;
+ }
+ return it->second;
+ }
+ auto find(const KeyT &needle) {
+ return std::find_if(table_.begin(), table_.end(), [&](auto &key) { return key.first == needle; });
+ }
+
+ private:
+ using KeyValue = value_type;
+ td::vector<KeyValue> table_;
+};
+
+template <class KeyT, class ValueT>
+class SortedVectorTable {
+ public:
+ using key_type = KeyT;
+ using value_type = std::pair<KeyT, ValueT>;
+ template <class It>
+ SortedVectorTable(It begin, It end) : table_(begin, end) {
+ std::sort(table_.begin(), table_.end());
+ }
+
+ ValueT &operator[](const KeyT &needle) {
+ auto it = std::lower_bound(table_.begin(), table_.end(), needle,
+ [](const auto &l, const auto &r) { return l.first < r; });
+ if (it == table_.end() || it->first != needle) {
+ it = table_.insert(it, {needle, ValueT{}});
+ }
+ return it->second;
+ }
+
+ auto find(const KeyT &needle) {
+ auto it = std::lower_bound(table_.begin(), table_.end(), needle,
+ [](const auto &l, const auto &r) { return l.first < r; });
+ if (it != table_.end() && it->first == needle) {
+ return it;
+ }
+ return table_.end();
+ }
+
+ private:
+ using KeyValue = value_type;
+ td::vector<KeyValue> table_;
+};
+
+template <class KeyT, class ValueT, class HashT = td::Hash<KeyT>>
+class SimpleHashTable {
+ public:
+ using key_type = KeyT;
+ using value_type = std::pair<KeyT, ValueT>;
+ template <class It>
+ SimpleHashTable(It begin, It end) {
+ nodes_.resize((end - begin) * 2);
+ for (; begin != end; ++begin) {
+ insert(begin->first, begin->second);
+ }
+ }
+
+ ValueT &operator[](const KeyT &needle) {
+ UNREACHABLE();
+ }
+
+ ValueT *find(const KeyT &needle) {
+ auto hash = HashT()(needle);
+ std::size_t i = hash % nodes_.size();
+ while (true) {
+ if (nodes_[i].key == needle) {
+ return &nodes_[i].value;
+ }
+ if (nodes_[i].hash == 0) {
+ return nullptr;
+ }
+ i++;
+ if (i == nodes_.size()) {
+ i = 0;
+ }
+ }
+ }
+
+ private:
+ using KeyValue = value_type;
+ struct Node {
+ std::size_t hash{0};
+ KeyT key;
+ ValueT value;
+ };
+ td::vector<Node> nodes_;
+
+ void insert(KeyT key, ValueT value) {
+ auto hash = HashT()(key);
+ std::size_t i = hash % nodes_.size();
+ while (true) {
+ if (nodes_[i].hash == 0 || (nodes_[i].hash == hash && nodes_[i].key == key)) {
+ nodes_[i].value = value;
+ nodes_[i].key = key;
+ nodes_[i].hash = hash;
+ return;
+ }
+ i++;
+ if (i == nodes_.size()) {
+ i = 0;
+ }
+ }
+ }
+};
+
+template <typename TableT>
+static void BM_Get(benchmark::State &state) {
+ std::size_t n = state.range(0);
+ constexpr std::size_t BATCH_SIZE = 1024;
+ td::Random::Xorshift128plus rnd(123);
+ using Key = typename TableT::key_type;
+ using Value = typename TableT::value_type::second_type;
+ using KeyValue = std::pair<Key, Value>;
+ td::vector<KeyValue> data;
+ td::vector<Key> keys;
+
+ TableT table;
+ for (std::size_t i = 0; i < n; i++) {
+ auto key = rnd();
+ auto value = rnd();
+ data.emplace_back(key, value);
+ table.emplace(key, value);
+ keys.push_back(key);
+ }
+
+ std::size_t key_i = 0;
+ td::random_shuffle(td::as_mutable_span(keys), rnd);
+ auto next_key = [&] {
+ key_i++;
+ if (key_i == data.size()) {
+ key_i = 0;
+ }
+ return keys[key_i];
+ };
+
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ benchmark::DoNotOptimize(table.find(next_key()));
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_find_same(benchmark::State &state) {
+ td::Random::Xorshift128plus rnd(123);
+ TableT table;
+ constexpr std::size_t N = 100000;
+ constexpr std::size_t BATCH_SIZE = 1024;
+ reserve(table, N);
+
+ for (std::size_t i = 0; i < N; i++) {
+ table.emplace(rnd(), i);
+ }
+
+ auto key = td::Random::secure_uint64();
+ table[key] = 123;
+
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ benchmark::DoNotOptimize(table.find(key));
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_emplace_same(benchmark::State &state) {
+ td::Random::Xorshift128plus rnd(123);
+ TableT table;
+ constexpr std::size_t N = 100000;
+ constexpr std::size_t BATCH_SIZE = 1024;
+ reserve(table, N);
+
+ for (std::size_t i = 0; i < N; i++) {
+ table.emplace(rnd(), i);
+ }
+
+ auto key = 123743;
+ table[key] = 123;
+
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ benchmark::DoNotOptimize(table.emplace(key + (i & 15) * 100, 43784932));
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_emplace_string(benchmark::State &state) {
+ td::Random::Xorshift128plus rnd(123);
+ TableT table;
+ constexpr std::size_t N = 100000;
+ constexpr std::size_t BATCH_SIZE = 1024;
+ reserve(table, N);
+
+ for (std::size_t i = 0; i < N; i++) {
+ table.emplace(td::to_string(rnd()), i);
+ }
+
+ table["0"] = 123;
+ td::vector<td::string> strings;
+ for (std::size_t i = 0; i < 16; i++) {
+ strings.emplace_back(1, static_cast<char>('0' + i));
+ }
+
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ benchmark::DoNotOptimize(table.emplace(strings[i & 15], 43784932));
+ }
+ }
+}
+
+namespace td {
+template <class K, class V, class FunctT>
+static void table_remove_if(absl::flat_hash_map<K, V> &table, FunctT &&func) {
+ for (auto it = table.begin(); it != table.end();) {
+ if (func(*it)) {
+ auto copy = it;
+ ++it;
+ table.erase(copy);
+ } else {
+ ++it;
+ }
+ }
+}
+} // namespace td
+
+template <typename TableT>
+static void BM_remove_if(benchmark::State &state) {
+ constexpr std::size_t N = 100000;
+ constexpr std::size_t BATCH_SIZE = N;
+
+ TableT table;
+ reserve(table, N);
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ state.PauseTiming();
+ td::Random::Xorshift128plus rnd(123);
+ for (std::size_t i = 0; i < N; i++) {
+ table.emplace(rnd(), i);
+ }
+ state.ResumeTiming();
+
+ td::table_remove_if(table, [](auto &it) { return it.second % 2 == 0; });
+ }
+}
+
+template <typename TableT>
+static void BM_erase_all_with_begin(benchmark::State &state) {
+ constexpr std::size_t N = 100000;
+ constexpr std::size_t BATCH_SIZE = N;
+
+ TableT table;
+ td::Random::Xorshift128plus rnd(123);
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ table.emplace(rnd() + 1, i);
+ }
+ while (!table.empty()) {
+ table.erase(table.begin());
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_cache(benchmark::State &state) {
+ constexpr std::size_t N = 1000;
+ constexpr std::size_t BATCH_SIZE = 1000000;
+
+ TableT table;
+ td::Random::Xorshift128plus rnd(123);
+ td::VectorQueue<td::uint64> keys;
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ auto key = rnd() + 1;
+ keys.push(key);
+ table.emplace(key, i);
+ if (table.size() > N) {
+ table.erase(keys.pop());
+ }
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_cache2(benchmark::State &state) {
+ constexpr std::size_t N = 1000;
+ constexpr std::size_t BATCH_SIZE = 1000000;
+
+ TableT table;
+ td::Random::Xorshift128plus rnd(123);
+ td::VectorQueue<td::uint64> keys;
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ auto key = rnd() + 1;
+ keys.push(key);
+ table.emplace(key, i);
+ if (table.size() > N) {
+ table.erase(keys.pop_rand(rnd));
+ }
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_cache3(benchmark::State &state) {
+ std::size_t N = state.range(0);
+ constexpr std::size_t BATCH_SIZE = 1000000;
+
+ TableT table;
+ td::Random::Xorshift128plus rnd(123);
+ td::VectorQueue<td::uint64> keys;
+ std::size_t step = 20;
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i += step) {
+ auto key = rnd() + 1;
+ keys.push(key);
+ table.emplace(key, i);
+
+ for (std::size_t j = 1; j < step; j++) {
+ auto key_to_find = keys.data()[rnd() % keys.size()];
+ benchmark::DoNotOptimize(table.find(key_to_find));
+ }
+
+ if (table.size() > N) {
+ table.erase(keys.pop_rand(rnd));
+ }
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_remove_if_slow(benchmark::State &state) {
+ constexpr std::size_t N = 5000;
+ constexpr std::size_t BATCH_SIZE = 500000;
+
+ TableT table;
+ td::Random::Xorshift128plus rnd(123);
+ for (std::size_t i = 0; i < N; i++) {
+ table.emplace(rnd() + 1, i);
+ }
+ auto first_key = table.begin()->first;
+ {
+ std::size_t cnt = 0;
+ td::table_remove_if(table, [&cnt, n = N](auto &) {
+ cnt += 2;
+ return cnt <= n;
+ });
+ }
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ table.emplace(first_key, i);
+ table.erase(first_key);
+ }
+ }
+}
+
+template <typename TableT>
+static void BM_remove_if_slow_old(benchmark::State &state) {
+ constexpr std::size_t N = 100000;
+ constexpr std::size_t BATCH_SIZE = 5000000;
+
+ TableT table;
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ td::Random::Xorshift128plus rnd(123);
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ table.emplace(rnd() + 1, i);
+ if (table.size() > N) {
+ std::size_t cnt = 0;
+ td::table_remove_if(table, [&cnt, n = N](auto &) {
+ cnt += 2;
+ return cnt <= n;
+ });
+ }
+ }
+ }
+}
+
+template <typename TableT>
+static void benchmark_create(td::Slice name) {
+ td::Random::Xorshift128plus rnd(123);
+ {
+ constexpr std::size_t N = 10000000;
+ TableT table;
+ reserve(table, N);
+ auto start = td::Timestamp::now();
+ for (std::size_t i = 0; i < N; i++) {
+ table.emplace(rnd(), i);
+ }
+ auto end = td::Timestamp::now();
+ LOG(INFO) << name << ": create " << N << " elements: " << td::format::as_time(end.at() - start.at());
+
+ double res = 0;
+ td::vector<std::pair<std::size_t, td::format::Time>> pauses;
+ for (std::size_t i = 0; i < N; i++) {
+ auto emplace_start = td::Timestamp::now();
+ table.emplace(rnd(), i);
+ auto emplace_end = td::Timestamp::now();
+ auto pause = emplace_end.at() - emplace_start.at();
+ res = td::max(pause, res);
+ if (pause > 0.001) {
+ pauses.emplace_back(i, td::format::as_time(pause));
+ }
+ }
+
+ LOG(INFO) << name << ": create another " << N << " elements, max pause = " << td::format::as_time(res) << " "
+ << pauses;
+ }
+}
+
+struct CacheMissNode {
+ td::uint32 data{};
+ char padding[64 - sizeof(data)];
+};
+
+class IterateFast {
+ public:
+ static td::uint32 iterate(CacheMissNode *ptr, std::size_t max_shift) {
+ td::uint32 res = 1;
+ for (std::size_t i = 0; i < max_shift; i++) {
+ if (ptr[i].data % max_shift != 0) {
+ res *= ptr[i].data;
+ } else {
+ res /= ptr[i].data;
+ }
+ }
+ return res;
+ }
+};
+
+class IterateSlow {
+ public:
+ static td::uint32 iterate(CacheMissNode *ptr, std::size_t max_shift) {
+ td::uint32 res = 1;
+ for (std::size_t i = 0;; i++) {
+ if (ptr[i].data % max_shift != 0) {
+ res *= ptr[i].data;
+ } else {
+ break;
+ }
+ }
+ return res;
+ }
+};
+
+template <class F>
+static void BM_cache_miss(benchmark::State &state) {
+ td::uint32 max_shift = state.range(0);
+ bool flag = state.range(1);
+ std::random_device rd;
+ std::mt19937 rnd(rd());
+ int N = 50000000;
+ td::vector<CacheMissNode> nodes(N);
+ td::uint32 i = 0;
+ for (auto &node : nodes) {
+ if (flag) {
+ node.data = i++ % max_shift;
+ } else {
+ node.data = rnd();
+ }
+ }
+
+ td::vector<int> positions(N);
+ std::uniform_int_distribution<td::uint32> rnd_pos(0, N - 1000);
+ for (auto &pos : positions) {
+ pos = rnd_pos(rnd);
+ if (flag) {
+ pos = pos / max_shift * max_shift + 1;
+ }
+ }
+
+ while (state.KeepRunningBatch(positions.size())) {
+ for (const auto pos : positions) {
+ auto *ptr = &nodes[pos];
+ auto res = F::iterate(ptr, max_shift);
+ benchmark::DoNotOptimize(res);
+ }
+ }
+}
+
+static td::uint64 equal_mask_slow(td::uint8 *bytes, td::uint8 needle) {
+ td::uint64 mask = 0;
+ for (int i = 0; i < 16; i++) {
+ mask |= (bytes[i] == needle) << i;
+ }
+ return mask;
+}
+
+template <class MaskT>
+static void BM_mask(benchmark::State &state) {
+ std::size_t BATCH_SIZE = 1024;
+ td::vector<td::uint8> bytes(BATCH_SIZE + 16);
+ for (auto &b : bytes) {
+ b = static_cast<td::uint8>(td::Random::fast(0, 17));
+ }
+
+ while (state.KeepRunningBatch(BATCH_SIZE)) {
+ for (std::size_t i = 0; i < BATCH_SIZE; i++) {
+ benchmark::DoNotOptimize(MaskT::equal_mask(bytes.data() + i, 17));
+ }
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_mask, td::MaskPortable);
+#ifdef __aarch64__
+BENCHMARK_TEMPLATE(BM_mask, td::MaskNeonFolly);
+BENCHMARK_TEMPLATE(BM_mask, td::MaskNeon);
+#endif
+#if TD_SSE2
+BENCHMARK_TEMPLATE(BM_mask, td::MaskSse2);
+#endif
+
+template <class KeyT, class ValueT, class HashT = td::Hash<KeyT>, class EqT = std::equal_to<KeyT>>
+using FlatHashMapImpl = td::FlatHashTable<td::MapNode<KeyT, ValueT>, HashT, EqT>;
+
+#define FOR_EACH_TABLE(F) \
+ F(FlatHashMapImpl) \
+ F(td::FlatHashMapChunks) \
+ F(folly::F14FastMap) \
+ F(absl::flat_hash_map) \
+ F(std::unordered_map) \
+ F(std::map)
+
+//BENCHMARK(BM_cache_miss<IterateSlow>)->Ranges({{1, 16}, {0, 1}});
+//BENCHMARK(BM_cache_miss<IterateFast>)->Ranges({{1, 16}, {0, 1}});
+//BENCHMARK_TEMPLATE(BM_Get, VectorTable<td::uint64, td::uint64>)->Range(1, 1 << 26);
+//BENCHMARK_TEMPLATE(BM_Get, SortedVectorTable<td::uint64, td::uint64>)->Range(1, 1 << 26);
+//BENCHMARK_TEMPLATE(BM_Get, NoOpTable<td::uint64, td::uint64>)->Range(1, 1 << 26);
+
+#define REGISTER_GET_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_Get, HT<td::uint64, td::uint64>)->Range(1, 1 << 23);
+
+#define REGISTER_FIND_BENCHMARK(HT) \
+ BENCHMARK_TEMPLATE(BM_find_same, HT<td::uint64, td::uint64>) \
+ ->ComputeStatistics("max", [](const td::vector<double> &v) { return *std::max_element(v.begin(), v.end()); }) \
+ ->ComputeStatistics("min", [](const td::vector<double> &v) { return *std::min_element(v.begin(), v.end()); }) \
+ ->Repetitions(20) \
+ ->DisplayAggregatesOnly(true);
+
+#define REGISTER_REMOVE_IF_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_remove_if, HT<td::uint64, td::uint64>);
+#define REGISTER_EMPLACE_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_emplace_same, HT<td::uint64, td::uint64>);
+#define REGISTER_EMPLACE_STRING_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_emplace_string, HT<td::string, td::uint64>);
+#define REGISTER_CACHE_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_cache, HT<td::uint64, td::uint64>);
+#define REGISTER_CACHE2_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_cache2, HT<td::uint64, td::uint64>);
+#define REGISTER_CACHE3_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_cache3, HT<td::uint64, td::uint64>)->Range(1, 1 << 23);
+#define REGISTER_ERASE_ALL_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_erase_all_with_begin, HT<td::uint64, td::uint64>);
+#define REGISTER_REMOVE_IF_SLOW_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_remove_if_slow, HT<td::uint64, td::uint64>);
+#define REGISTER_REMOVE_IF_SLOW_OLD_BENCHMARK(HT) BENCHMARK_TEMPLATE(BM_remove_if_slow_old, HT<td::uint64, td::uint64>);
+
+FOR_EACH_TABLE(REGISTER_GET_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_CACHE3_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_CACHE2_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_CACHE_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_REMOVE_IF_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_EMPLACE_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_EMPLACE_STRING_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_ERASE_ALL_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_FIND_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_REMOVE_IF_SLOW_OLD_BENCHMARK)
+FOR_EACH_TABLE(REGISTER_REMOVE_IF_SLOW_BENCHMARK)
+
+#define RUN_CREATE_BENCHMARK(HT) benchmark_create<HT<td::uint64, td::uint64>>(#HT);
+
+int main(int argc, char **argv) {
+ // FOR_EACH_TABLE(RUN_CREATE_BENCHMARK);
+
+ benchmark::Initialize(&argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ benchmark::Shutdown();
+}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/heap.cpp b/protocols/Telegram/tdlib/td/tdutils/test/heap.cpp
index 0dcfcf98ff..02b6d81424 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/heap.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/heap.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -8,29 +8,24 @@
#include "td/utils/common.h"
#include "td/utils/Heap.h"
-#include "td/utils/logging.h"
#include "td/utils/Random.h"
+#include "td/utils/Span.h"
-#include <algorithm>
#include <cstdio>
-#include <cstdlib>
#include <set>
#include <utility>
-REGISTER_TESTS(heap)
-
-using namespace td;
-
TEST(Heap, sort_random_perm) {
int n = 1000000;
- std::vector<int> v(n);
+
+ td::vector<int> v(n);
for (int i = 0; i < n; i++) {
v[i] = i;
}
- std::srand(123);
- std::random_shuffle(v.begin(), v.end());
- std::vector<HeapNode> nodes(n);
- KHeap<int> kheap;
+ td::Random::Xorshift128plus rnd(123);
+ td::random_shuffle(td::as_mutable_span(v), rnd);
+ td::vector<td::HeapNode> nodes(n);
+ td::KHeap<int> kheap;
for (int i = 0; i < n; i++) {
kheap.insert(v[i], &nodes[i]);
}
@@ -38,7 +33,7 @@ TEST(Heap, sort_random_perm) {
ASSERT_EQ(i, kheap.top_key());
kheap.pop();
}
-};
+}
class CheckedHeap {
public:
@@ -51,7 +46,7 @@ class CheckedHeap {
nodes[i].value = i;
}
}
- static void xx(int key, const HeapNode *heap_node) {
+ static void xx(int key, const td::HeapNode *heap_node) {
const Node *node = static_cast<const Node *>(heap_node);
std::fprintf(stderr, "(%d;%d)", node->key, node->value);
}
@@ -66,9 +61,9 @@ class CheckedHeap {
}
int random_id() const {
CHECK(!empty());
- return ids[Random::fast(0, static_cast<int>(ids.size() - 1))];
+ return ids[td::Random::fast(0, static_cast<int>(ids.size() - 1))];
}
- size_t size() const {
+ std::size_t size() const {
return ids.size();
}
bool empty() const {
@@ -140,19 +135,19 @@ class CheckedHeap {
}
private:
- struct Node : public HeapNode {
+ struct Node final : public td::HeapNode {
Node() = default;
Node(int key, int value) : key(key), value(value) {
}
int key = 0;
int value = 0;
};
- vector<int> ids;
- vector<int> rev_ids;
- vector<int> free_ids;
- vector<Node> nodes;
+ td::vector<int> ids;
+ td::vector<int> rev_ids;
+ td::vector<int> free_ids;
+ td::vector<Node> nodes;
std::set<std::pair<int, int>> set_heap;
- KHeap<int> kheap;
+ td::KHeap<int> kheap;
};
TEST(Heap, random_events) {
@@ -163,11 +158,11 @@ TEST(Heap, random_events) {
heap.top_key();
}
- int x = Random::fast(0, 4);
+ int x = td::Random::fast(0, 4);
if (heap.empty() || (x < 2 && heap.size() < 1000)) {
- heap.insert(Random::fast(0, 99));
+ heap.insert(td::Random::fast(0, 99));
} else if (x < 3) {
- heap.fix_key(Random::fast(0, 99), heap.random_id());
+ heap.fix_key(td::Random::fast(0, 99), heap.random_id());
} else if (x < 4) {
heap.erase(heap.random_id());
} else if (x < 5) {
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/json.cpp b/protocols/Telegram/tdlib/td/tdutils/test/json.cpp
index deca81a791..4e57b2d562 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/json.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/json.cpp
@@ -1,31 +1,26 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
-#include "td/utils/tests.h"
-
#include "td/utils/JsonBuilder.h"
#include "td/utils/logging.h"
+#include "td/utils/Slice.h"
#include "td/utils/StringBuilder.h"
+#include "td/utils/tests.h"
-#include <tuple>
#include <utility>
-REGISTER_TESTS(json)
-
-using namespace td;
-
-static void decode_encode(string str, string result = "") {
+static void decode_encode(const td::string &str, td::string result = td::string()) {
auto str_copy = str;
- auto r_value = json_decode(str_copy);
+ auto r_value = td::json_decode(str_copy);
ASSERT_TRUE(r_value.is_ok());
if (r_value.is_error()) {
LOG(INFO) << r_value.error();
return;
}
- auto new_str = json_encode<string>(r_value.ok());
+ auto new_str = td::json_encode<td::string>(r_value.ok());
if (result.empty()) {
result = str;
}
@@ -34,21 +29,22 @@ static void decode_encode(string str, string result = "") {
TEST(JSON, array) {
char tmp[1000];
- StringBuilder sb({tmp, sizeof(tmp)});
- JsonBuilder jb(std::move(sb));
+ td::StringBuilder sb(td::MutableSlice{tmp, sizeof(tmp)});
+ td::JsonBuilder jb(std::move(sb));
jb.enter_value().enter_array() << "Hello" << -123;
ASSERT_EQ(jb.string_builder().is_error(), false);
auto encoded = jb.string_builder().as_cslice().str();
ASSERT_EQ("[\"Hello\",-123]", encoded);
decode_encode(encoded);
}
+
TEST(JSON, object) {
char tmp[1000];
- StringBuilder sb({tmp, sizeof(tmp)});
- JsonBuilder jb(std::move(sb));
+ td::StringBuilder sb(td::MutableSlice{tmp, sizeof(tmp)});
+ td::JsonBuilder jb(std::move(sb));
auto c = jb.enter_object();
- c << std::tie("key", "value");
- c << std::make_pair("1", 2);
+ c("key", "value");
+ c("1", 2);
c.leave();
ASSERT_EQ(jb.string_builder().is_error(), false);
auto encoded = jb.string_builder().as_cslice().str();
@@ -58,8 +54,8 @@ TEST(JSON, object) {
TEST(JSON, nested) {
char tmp[1000];
- StringBuilder sb({tmp, sizeof(tmp)});
- JsonBuilder jb(std::move(sb));
+ td::StringBuilder sb(td::MutableSlice{tmp, sizeof(tmp)});
+ td::JsonBuilder jb(std::move(sb));
{
auto a = jb.enter_array();
a << 1;
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/log.cpp b/protocols/Telegram/tdlib/td/tdutils/test/log.cpp
new file mode 100644
index 0000000000..34af21353c
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/log.cpp
@@ -0,0 +1,187 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/AsyncFileLog.h"
+#include "td/utils/benchmark.h"
+#include "td/utils/CombinedLog.h"
+#include "td/utils/FileLog.h"
+#include "td/utils/format.h"
+#include "td/utils/logging.h"
+#include "td/utils/MemoryLog.h"
+#include "td/utils/NullLog.h"
+#include "td/utils/port/path.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/tests.h"
+#include "td/utils/TsFileLog.h"
+#include "td/utils/TsLog.h"
+
+#include <functional>
+#include <limits>
+
+char disable_linker_warning_about_empty_file_tdutils_test_log_cpp TD_UNUSED;
+
+#if !TD_THREAD_UNSUPPORTED
+template <class Log>
+class LogBenchmark final : public td::Benchmark {
+ public:
+ LogBenchmark(std::string name, int threads_n, bool test_full_logging, std::function<td::unique_ptr<Log>()> creator)
+ : name_(std::move(name))
+ , threads_n_(threads_n)
+ , test_full_logging_(test_full_logging)
+ , creator_(std::move(creator)) {
+ }
+ std::string get_description() const final {
+ return PSTRING() << name_ << " " << (test_full_logging_ ? "ERROR" : "PLAIN") << " "
+ << td::tag("threads_n", threads_n_);
+ }
+ void start_up() final {
+ log_ = creator_();
+ threads_.resize(threads_n_);
+ }
+ void tear_down() final {
+ if (log_ == nullptr) {
+ return;
+ }
+ for (const auto &path : log_->get_file_paths()) {
+ td::unlink(path).ignore();
+ }
+ log_.reset();
+ }
+ void run(int n) final {
+ auto old_log_interface = td::log_interface;
+ if (log_ != nullptr) {
+ td::log_interface = log_.get();
+ }
+
+ for (auto &thread : threads_) {
+ thread = td::thread([this, n] { this->run_thread(n); });
+ }
+ for (auto &thread : threads_) {
+ thread.join();
+ }
+
+ td::log_interface = old_log_interface;
+ }
+
+ void run_thread(int n) {
+ auto str = PSTRING() << "#" << n << " : fsjklfdjsklfjdsklfjdksl\n";
+ for (int i = 0; i < n; i++) {
+ if (i % 10000 == 0 && log_ != nullptr) {
+ log_->after_rotation();
+ }
+ if (test_full_logging_) {
+ LOG(ERROR) << str;
+ } else {
+ LOG(PLAIN) << str;
+ }
+ }
+ }
+
+ private:
+ std::string name_;
+ td::unique_ptr<td::LogInterface> log_;
+ int threads_n_{0};
+ bool test_full_logging_{false};
+ std::function<td::unique_ptr<Log>()> creator_;
+ std::vector<td::thread> threads_;
+};
+
+template <class F>
+static void bench_log(std::string name, F &&f) {
+ for (auto test_full_logging : {false, true}) {
+ for (auto threads_n : {1, 4, 8}) {
+ bench(LogBenchmark<typename decltype(f())::element_type>(name, threads_n, test_full_logging, f));
+ }
+ }
+}
+
+TEST(Log, Bench) {
+ bench_log("NullLog", [] { return td::make_unique<td::NullLog>(); });
+
+ // bench_log("Default", []() -> td::unique_ptr<td::NullLog> { return nullptr; });
+
+ bench_log("MemoryLog", [] { return td::make_unique<td::MemoryLog<1 << 20>>(); });
+
+ bench_log("CombinedLogEmpty", [] { return td::make_unique<td::CombinedLog>(); });
+
+ bench_log("CombinedLogMemory", [] {
+ auto result = td::make_unique<td::CombinedLog>();
+ static td::NullLog null_log;
+ static td::MemoryLog<1 << 20> memory_log;
+ result->set_first(&null_log);
+ result->set_second(&memory_log);
+ result->set_first_verbosity_level(VERBOSITY_NAME(DEBUG));
+ result->set_second_verbosity_level(VERBOSITY_NAME(DEBUG));
+ return result;
+ });
+
+ bench_log("TsFileLog",
+ [] { return td::TsFileLog::create("tmplog", std::numeric_limits<td::int64>::max(), false).move_as_ok(); });
+
+ bench_log("FileLog + TsLog", [] {
+ class FileLog final : public td::LogInterface {
+ public:
+ FileLog() {
+ file_log_.init("tmplog", std::numeric_limits<td::int64>::max(), false).ensure();
+ ts_log_.init(&file_log_);
+ }
+ void do_append(int log_level, td::CSlice slice) final {
+ static_cast<td::LogInterface &>(ts_log_).do_append(log_level, slice);
+ }
+ std::vector<std::string> get_file_paths() final {
+ return file_log_.get_file_paths();
+ }
+
+ private:
+ td::FileLog file_log_;
+ td::TsLog ts_log_{nullptr};
+ };
+ return td::make_unique<FileLog>();
+ });
+
+ bench_log("FileLog", [] {
+ class FileLog final : public td::LogInterface {
+ public:
+ FileLog() {
+ file_log_.init("tmplog", std::numeric_limits<td::int64>::max(), false).ensure();
+ }
+ void do_append(int log_level, td::CSlice slice) final {
+ static_cast<td::LogInterface &>(file_log_).do_append(log_level, slice);
+ }
+ std::vector<std::string> get_file_paths() final {
+ return file_log_.get_file_paths();
+ }
+
+ private:
+ td::FileLog file_log_;
+ };
+ return td::make_unique<FileLog>();
+ });
+
+#if !TD_EVENTFD_UNSUPPORTED
+ bench_log("AsyncFileLog", [] {
+ class AsyncFileLog final : public td::LogInterface {
+ public:
+ AsyncFileLog() {
+ file_log_.init("tmplog", std::numeric_limits<td::int64>::max()).ensure();
+ }
+ void do_append(int log_level, td::CSlice slice) final {
+ static_cast<td::LogInterface &>(file_log_).do_append(log_level, slice);
+ }
+ std::vector<std::string> get_file_paths() final {
+ return static_cast<td::LogInterface &>(file_log_).get_file_paths();
+ }
+
+ private:
+ td::AsyncFileLog file_log_;
+ };
+ return td::make_unique<AsyncFileLog>();
+ });
+#endif
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/misc.cpp b/protocols/Telegram/tdlib/td/tdutils/test/misc.cpp
index dd1f1ec457..7db990dad1 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/misc.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/misc.cpp
@@ -1,47 +1,91 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
+#include "td/utils/algorithm.h"
+#include "td/utils/as.h"
#include "td/utils/base64.h"
-#include "td/utils/HttpUrl.h"
+#include "td/utils/BigNum.h"
+#include "td/utils/bits.h"
+#include "td/utils/CancellationToken.h"
+#include "td/utils/common.h"
+#include "td/utils/ExitGuard.h"
+#include "td/utils/FloodControlFast.h"
+#include "td/utils/Hash.h"
+#include "td/utils/HashMap.h"
+#include "td/utils/HashSet.h"
+#include "td/utils/HashTableUtils.h"
+#include "td/utils/invoke.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/port/EventFd.h"
#include "td/utils/port/FileFd.h"
+#include "td/utils/port/IPAddress.h"
#include "td/utils/port/path.h"
#include "td/utils/port/sleep.h"
#include "td/utils/port/Stat.h"
#include "td/utils/port/thread.h"
+#include "td/utils/port/uname.h"
+#include "td/utils/port/wstring_convert.h"
#include "td/utils/Random.h"
#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#include "td/utils/tests.h"
+#include "td/utils/Time.h"
+#include "td/utils/tl_helpers.h"
+#include "td/utils/translit.h"
+#include "td/utils/uint128.h"
+#include "td/utils/unicode.h"
+#include "td/utils/utf8.h"
+#include <algorithm>
#include <atomic>
-#include <clocale>
#include <limits>
#include <locale>
+#include <unordered_map>
+#include <utility>
-using namespace td;
+#if TD_HAVE_ABSL
+#include <absl/container/flat_hash_map.h>
+#include <absl/hash/hash.h>
+#endif
+
+struct CheckExitGuard {
+ explicit CheckExitGuard(bool expected_value) : expected_value_(expected_value) {
+ }
+ CheckExitGuard(CheckExitGuard &&) = delete;
+ CheckExitGuard &operator=(CheckExitGuard &&) = delete;
+ CheckExitGuard(const CheckExitGuard &) = delete;
+ CheckExitGuard &operator=(const CheckExitGuard &) = delete;
+ ~CheckExitGuard() {
+ ASSERT_EQ(expected_value_, td::ExitGuard::is_exited());
+ }
+
+ bool expected_value_;
+};
+
+static CheckExitGuard check_exit_guard_true{true};
+static td::ExitGuard exit_guard;
#if TD_LINUX || TD_DARWIN
TEST(Misc, update_atime_saves_mtime) {
- SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
- std::string name = "test_file";
- unlink(name).ignore();
- auto r_file = FileFd::open(name, FileFd::Read | FileFd::Flags::Create | FileFd::Flags::Truncate);
+ td::string name = "test_file";
+ td::unlink(name).ignore();
+ auto r_file = td::FileFd::open(name, td::FileFd::Read | td::FileFd::Flags::Create | td::FileFd::Flags::Truncate);
LOG_IF(ERROR, r_file.is_error()) << r_file.error();
ASSERT_TRUE(r_file.is_ok());
r_file.move_as_ok().close();
- auto info = stat(name).ok();
- int32 tests_ok = 0;
- int32 tests_wa = 0;
+ auto info = td::stat(name).ok();
+ td::int32 tests_ok = 0;
+ td::int32 tests_wa = 0;
for (int i = 0; i < 10000; i++) {
- update_atime(name).ensure();
- auto new_info = stat(name).ok();
+ td::update_atime(name).ensure();
+ auto new_info = td::stat(name).ok();
if (info.mtime_nsec_ == new_info.mtime_nsec_) {
tests_ok++;
} else {
@@ -49,31 +93,30 @@ TEST(Misc, update_atime_saves_mtime) {
info.mtime_nsec_ = new_info.mtime_nsec_;
}
ASSERT_EQ(info.mtime_nsec_, new_info.mtime_nsec_);
- usleep_for(Random::fast(0, 1000));
+ td::usleep_for(td::Random::fast(0, 1000));
}
if (tests_wa > 0) {
LOG(ERROR) << "Access time was unexpectedly updated " << tests_wa << " times";
}
- unlink(name).ensure();
+ td::unlink(name).ensure();
}
TEST(Misc, update_atime_change_atime) {
- SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR));
- std::string name = "test_file";
- unlink(name).ignore();
- auto r_file = FileFd::open(name, FileFd::Read | FileFd::Flags::Create | FileFd::Flags::Truncate);
+ td::string name = "test_file";
+ td::unlink(name).ignore();
+ auto r_file = td::FileFd::open(name, td::FileFd::Read | td::FileFd::Flags::Create | td::FileFd::Flags::Truncate);
LOG_IF(ERROR, r_file.is_error()) << r_file.error();
ASSERT_TRUE(r_file.is_ok());
r_file.move_as_ok().close();
- auto info = stat(name).ok();
+ auto info = td::stat(name).ok();
// not enough for fat and e.t.c.
- usleep_for(5000000);
- update_atime(name).ensure();
- auto new_info = stat(name).ok();
+ td::usleep_for(5000000);
+ td::update_atime(name).ensure();
+ auto new_info = td::stat(name).ok();
if (info.atime_nsec_ == new_info.atime_nsec_) {
LOG(ERROR) << "Access time was unexpectedly not changed";
}
- unlink(name).ensure();
+ td::unlink(name).ensure();
}
#endif
@@ -84,9 +127,9 @@ TEST(Misc, errno_tls_bug) {
// CHECK(errno == 0);
#if !TD_THREAD_UNSUPPORTED && !TD_EVENTFD_UNSUPPORTED
- EventFd test_event_fd;
+ td::EventFd test_event_fd;
test_event_fd.init();
- std::atomic<int> s(0);
+ std::atomic<int> s{0};
s = 1;
td::thread th([&] {
while (s != 1) {
@@ -96,21 +139,21 @@ TEST(Misc, errno_tls_bug) {
th.join();
for (int i = 0; i < 1000; i++) {
- vector<EventFd> events(10);
- vector<td::thread> threads;
+ td::vector<td::EventFd> events(10);
+ td::vector<td::thread> threads;
for (auto &event : events) {
event.init();
event.release();
}
for (auto &event : events) {
- threads.push_back(td::thread([&] {
+ threads.emplace_back([&] {
{
- EventFd tmp;
+ td::EventFd tmp;
tmp.init();
tmp.acquire();
}
event.acquire();
- }));
+ });
}
for (auto &thread : threads) {
thread.join();
@@ -119,81 +162,281 @@ TEST(Misc, errno_tls_bug) {
#endif
}
+TEST(Misc, get_last_argument) {
+ auto a = td::make_unique<int>(5);
+ ASSERT_EQ(*td::get_last_argument(std::move(a)), 5);
+ ASSERT_EQ(*td::get_last_argument(1, 2, 3, 4, a), 5);
+ ASSERT_EQ(*td::get_last_argument(a), 5);
+ auto b = td::get_last_argument(1, 2, 3, std::move(a));
+ ASSERT_TRUE(!a);
+ ASSERT_EQ(*b, 5);
+}
+
+TEST(Misc, call_n_arguments) {
+ auto f = [](int, int) {
+ };
+ td::call_n_arguments<2>(f, 1, 3, 4);
+}
+
TEST(Misc, base64) {
- ASSERT_TRUE(is_base64("dGVzdA==") == true);
- ASSERT_TRUE(is_base64("dGVzdB==") == false);
- ASSERT_TRUE(is_base64("dGVzdA=") == false);
- ASSERT_TRUE(is_base64("dGVzdA") == false);
- ASSERT_TRUE(is_base64("dGVz") == true);
- ASSERT_TRUE(is_base64("") == true);
- ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == true);
- ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=") == false);
- ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
- ASSERT_TRUE(is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == false);
- ASSERT_TRUE(is_base64("====") == false);
-
- ASSERT_TRUE(is_base64url("dGVzdA==") == true);
- ASSERT_TRUE(is_base64url("dGVzdB==") == false);
- ASSERT_TRUE(is_base64url("dGVzdA=") == false);
- ASSERT_TRUE(is_base64url("dGVzdA") == true);
- ASSERT_TRUE(is_base64url("dGVz") == true);
- ASSERT_TRUE(is_base64url("") == true);
- ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == true);
- ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=") == false);
- ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
- ASSERT_TRUE(is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == false);
- ASSERT_TRUE(is_base64url("====") == false);
+ ASSERT_TRUE(td::is_base64("dGVzdA==") == true);
+ ASSERT_TRUE(td::is_base64("dGVzdB==") == false);
+ ASSERT_TRUE(td::is_base64("dGVzdA=") == false);
+ ASSERT_TRUE(td::is_base64("dGVzdA") == false);
+ ASSERT_TRUE(td::is_base64("dGVzd") == false);
+ ASSERT_TRUE(td::is_base64("dGVz") == true);
+ ASSERT_TRUE(td::is_base64("dGVz====") == false);
+ ASSERT_TRUE(td::is_base64("") == true);
+ ASSERT_TRUE(td::is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == true);
+ ASSERT_TRUE(td::is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=") == false);
+ ASSERT_TRUE(td::is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
+ ASSERT_TRUE(td::is_base64("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == false);
+ ASSERT_TRUE(td::is_base64("====") == false);
+
+ ASSERT_TRUE(td::is_base64url("dGVzdA==") == true);
+ ASSERT_TRUE(td::is_base64url("dGVzdB==") == false);
+ ASSERT_TRUE(td::is_base64url("dGVzdA=") == false);
+ ASSERT_TRUE(td::is_base64url("dGVzdA") == true);
+ ASSERT_TRUE(td::is_base64url("dGVzd") == false);
+ ASSERT_TRUE(td::is_base64url("dGVz") == true);
+ ASSERT_TRUE(td::is_base64url("dGVz====") == false);
+ ASSERT_TRUE(td::is_base64url("") == true);
+ ASSERT_TRUE(td::is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == true);
+ ASSERT_TRUE(td::is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=") == false);
+ ASSERT_TRUE(td::is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
+ ASSERT_TRUE(td::is_base64url("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == false);
+ ASSERT_TRUE(td::is_base64url("====") == false);
+
+ ASSERT_TRUE(td::is_base64_characters("dGVzdA==") == false);
+ ASSERT_TRUE(td::is_base64_characters("dGVzdB==") == false);
+ ASSERT_TRUE(td::is_base64_characters("dGVzdA=") == false);
+ ASSERT_TRUE(td::is_base64_characters("dGVzdA") == true);
+ ASSERT_TRUE(td::is_base64_characters("dGVz") == true);
+ ASSERT_TRUE(td::is_base64_characters("") == true);
+ ASSERT_TRUE(td::is_base64_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == true);
+ ASSERT_TRUE(td::is_base64_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=") == false);
+ ASSERT_TRUE(td::is_base64_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
+ ASSERT_TRUE(td::is_base64_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == false);
+ ASSERT_TRUE(td::is_base64_characters("====") == false);
+
+ ASSERT_TRUE(td::is_base64url_characters("dGVzdA==") == false);
+ ASSERT_TRUE(td::is_base64url_characters("dGVzdB==") == false);
+ ASSERT_TRUE(td::is_base64url_characters("dGVzdA=") == false);
+ ASSERT_TRUE(td::is_base64url_characters("dGVzdA") == true);
+ ASSERT_TRUE(td::is_base64url_characters("dGVz") == true);
+ ASSERT_TRUE(td::is_base64url_characters("") == true);
+ ASSERT_TRUE(td::is_base64url_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_") == true);
+ ASSERT_TRUE(td::is_base64url_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=") ==
+ false);
+ ASSERT_TRUE(td::is_base64url_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/") == false);
+ ASSERT_TRUE(td::is_base64url_characters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") == false);
+ ASSERT_TRUE(td::is_base64url_characters("====") == false);
for (int l = 0; l < 300000; l += l / 20 + l / 1000 * 500 + 1) {
for (int t = 0; t < 10; t++) {
- string s = rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), l);
- string encoded = base64url_encode(s);
- auto decoded = base64url_decode(encoded);
+ auto s = td::rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), l);
+ auto encoded = td::base64url_encode(s);
+ auto decoded = td::base64url_decode(encoded);
ASSERT_TRUE(decoded.is_ok());
ASSERT_TRUE(decoded.ok() == s);
- encoded = base64_encode(s);
- decoded = base64_decode(encoded);
+ encoded = td::base64_encode(s);
+ decoded = td::base64_decode(encoded);
ASSERT_TRUE(decoded.is_ok());
ASSERT_TRUE(decoded.ok() == s);
+
+ auto decoded_secure = td::base64_decode_secure(encoded);
+ ASSERT_TRUE(decoded_secure.is_ok());
+ ASSERT_TRUE(decoded_secure.ok().as_slice() == s);
}
}
- ASSERT_TRUE(base64url_decode("dGVzdA").is_ok());
- ASSERT_TRUE(base64url_decode("dGVzdB").is_error());
- ASSERT_TRUE(base64_encode(base64url_decode("dGVzdA").ok()) == "dGVzdA==");
- ASSERT_TRUE(base64_encode("any carnal pleas") == "YW55IGNhcm5hbCBwbGVhcw==");
- ASSERT_TRUE(base64_encode("any carnal pleasu") == "YW55IGNhcm5hbCBwbGVhc3U=");
- ASSERT_TRUE(base64_encode("any carnal pleasur") == "YW55IGNhcm5hbCBwbGVhc3Vy");
- ASSERT_TRUE(base64_encode(" /'.;.';≤.];,].',[.;/,.;/]/..;!@#!*(%?::;!%\";") ==
- "ICAgICAgLycuOy4nO+KJpC5dOyxdLicsWy47LywuOy9dLy4uOyFAIyEqKCU/"
- "Ojo7ISUiOw==");
+ ASSERT_TRUE(td::base64url_decode("dGVzdA").is_ok());
+ ASSERT_TRUE(td::base64url_decode("dGVzdB").is_error());
+ ASSERT_TRUE(td::base64_encode(td::base64url_decode("dGVzdA").ok()) == "dGVzdA==");
+ ASSERT_TRUE(td::base64_encode("any carnal pleas") == "YW55IGNhcm5hbCBwbGVhcw==");
+ ASSERT_TRUE(td::base64_encode("any carnal pleasu") == "YW55IGNhcm5hbCBwbGVhc3U=");
+ ASSERT_TRUE(td::base64_encode("any carnal pleasur") == "YW55IGNhcm5hbCBwbGVhc3Vy");
+ ASSERT_TRUE(td::base64_encode(" /'.;.';≤.];,].',[.;/,.;/]/..;!@#!*(%?::;!%\";") ==
+ "ICAgICAgLycuOy4nO+KJpC5dOyxdLicsWy47LywuOy9dLy4uOyFAIyEqKCU/Ojo7ISUiOw==");
+ ASSERT_TRUE(td::base64url_encode("ab><") == "YWI-PA");
+ ASSERT_TRUE(td::base64url_encode("ab><c") == "YWI-PGM");
+ ASSERT_TRUE(td::base64url_encode("ab><cd") == "YWI-PGNk");
+}
+
+template <class T>
+static void test_remove_if(td::vector<int> v, const T &func, const td::vector<int> &expected) {
+ td::remove_if(v, func);
+ if (expected != v) {
+ LOG(FATAL) << "Receive " << v << ", expected " << expected << " in remove_if";
+ }
+}
+
+TEST(Misc, remove_if) {
+ auto odd = [](int x) {
+ return x % 2 == 1;
+ };
+ auto even = [](int x) {
+ return x % 2 == 0;
+ };
+ auto all = [](int x) {
+ return true;
+ };
+ auto none = [](int x) {
+ return false;
+ };
+
+ td::vector<int> v{1, 2, 3, 4, 5, 6};
+ test_remove_if(v, odd, {2, 4, 6});
+ test_remove_if(v, even, {1, 3, 5});
+ test_remove_if(v, all, {});
+ test_remove_if(v, none, v);
+
+ v = td::vector<int>{1, 3, 5, 2, 4, 6};
+ test_remove_if(v, odd, {2, 4, 6});
+ test_remove_if(v, even, {1, 3, 5});
+ test_remove_if(v, all, {});
+ test_remove_if(v, none, v);
+
+ v.clear();
+ test_remove_if(v, odd, v);
+ test_remove_if(v, even, v);
+ test_remove_if(v, all, v);
+ test_remove_if(v, none, v);
+
+ v.push_back(-1);
+ test_remove_if(v, odd, v);
+ test_remove_if(v, even, v);
+ test_remove_if(v, all, {});
+ test_remove_if(v, none, v);
+
+ v[0] = 1;
+ test_remove_if(v, odd, {});
+ test_remove_if(v, even, v);
+ test_remove_if(v, all, {});
+ test_remove_if(v, none, v);
+
+ v[0] = 2;
+ test_remove_if(v, odd, v);
+ test_remove_if(v, even, {});
+ test_remove_if(v, all, {});
+ test_remove_if(v, none, v);
+}
+
+static void test_remove(td::vector<int> v, int value, const td::vector<int> &expected) {
+ bool is_found = expected != v;
+ ASSERT_EQ(is_found, td::remove(v, value));
+ if (expected != v) {
+ LOG(FATAL) << "Receive " << v << ", expected " << expected << " in remove";
+ }
+}
+
+TEST(Misc, remove) {
+ td::vector<int> v{1, 2, 3, 4, 5, 6};
+ test_remove(v, 0, {1, 2, 3, 4, 5, 6});
+ test_remove(v, 1, {2, 3, 4, 5, 6});
+ test_remove(v, 2, {1, 3, 4, 5, 6});
+ test_remove(v, 3, {1, 2, 4, 5, 6});
+ test_remove(v, 4, {1, 2, 3, 5, 6});
+ test_remove(v, 5, {1, 2, 3, 4, 6});
+ test_remove(v, 6, {1, 2, 3, 4, 5});
+ test_remove(v, 7, {1, 2, 3, 4, 5, 6});
+
+ v.clear();
+ test_remove(v, -1, v);
+ test_remove(v, 0, v);
+ test_remove(v, 1, v);
+}
+
+static void test_unique(td::vector<int> v, const td::vector<int> &expected) {
+ auto v_str = td::transform(v, &td::to_string<int>);
+ auto expected_str = td::transform(expected, &td::to_string<int>);
+
+ td::unique(v);
+ ASSERT_EQ(expected, v);
+
+ td::unique(v_str);
+ ASSERT_EQ(expected_str, v_str);
+}
+
+TEST(Misc, unique) {
+ test_unique({1, 2, 3, 4, 5, 6}, {1, 2, 3, 4, 5, 6});
+ test_unique({5, 2, 1, 6, 3, 4}, {1, 2, 3, 4, 5, 6});
+ test_unique({}, {});
+ test_unique({0}, {0});
+ test_unique({0, 0}, {0});
+ test_unique({0, 1}, {0, 1});
+ test_unique({1, 0}, {0, 1});
+ test_unique({1, 1}, {1});
+ test_unique({1, 1, 0, 0}, {0, 1});
+ test_unique({3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 0}, {0, 1, 2, 3});
+ test_unique({3, 3, 3, 3, 3}, {3});
+ test_unique({3, 3, -1, 3, 3}, {-1, 3});
+}
+
+TEST(Misc, contains) {
+ td::vector<int> v{1, 3, 5, 7, 4, 2};
+ for (int i = -10; i < 20; i++) {
+ ASSERT_EQ(td::contains(v, i), (1 <= i && i <= 5) || i == 7);
+ }
+
+ v.clear();
+ ASSERT_TRUE(!td::contains(v, 0));
+ ASSERT_TRUE(!td::contains(v, 1));
+
+ td::string str = "abacaba";
+ ASSERT_TRUE(!td::contains(str, '0'));
+ ASSERT_TRUE(!td::contains(str, 0));
+ ASSERT_TRUE(!td::contains(str, 'd'));
+ ASSERT_TRUE(td::contains(str, 'a'));
+ ASSERT_TRUE(td::contains(str, 'b'));
+ ASSERT_TRUE(td::contains(str, 'c'));
+}
+
+TEST(Misc, base32) {
+ ASSERT_EQ("", td::base32_encode(""));
+ ASSERT_EQ("me", td::base32_encode("a"));
+ td::base32_decode("me").ensure();
+ ASSERT_EQ("mfra", td::base32_encode("ab"));
+ ASSERT_EQ("mfrgg", td::base32_encode("abc"));
+ ASSERT_EQ("mfrggza", td::base32_encode("abcd"));
+ ASSERT_EQ("mfrggzdg", td::base32_encode("abcdf"));
+ ASSERT_EQ("mfrggzdgm4", td::base32_encode("abcdfg"));
+ for (int l = 0; l < 300000; l += l / 20 + l / 1000 * 500 + 1) {
+ for (int t = 0; t < 10; t++) {
+ auto s = td::rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), l);
+ auto encoded = td::base32_encode(s);
+ auto decoded = td::base32_decode(encoded);
+ ASSERT_TRUE(decoded.is_ok());
+ ASSERT_TRUE(decoded.ok() == s);
+ }
+ }
}
TEST(Misc, to_integer) {
- ASSERT_EQ(to_integer<int32>("-1234567"), -1234567);
- ASSERT_EQ(to_integer<int64>("-1234567"), -1234567);
- ASSERT_EQ(to_integer<uint32>("-1234567"), 0u);
- ASSERT_EQ(to_integer<int16>("-1234567"), 10617);
- ASSERT_EQ(to_integer<uint16>("-1234567"), 0u);
- ASSERT_EQ(to_integer<int16>("-1254567"), -9383);
- ASSERT_EQ(to_integer<uint16>("1254567"), 9383u);
- ASSERT_EQ(to_integer<int64>("-12345678910111213"), -12345678910111213);
- ASSERT_EQ(to_integer<uint64>("12345678910111213"), 12345678910111213ull);
-
- ASSERT_EQ(to_integer_safe<int32>("-1234567").ok(), -1234567);
- ASSERT_EQ(to_integer_safe<int64>("-1234567").ok(), -1234567);
- ASSERT_TRUE(to_integer_safe<uint32>("-1234567").is_error());
- ASSERT_TRUE(to_integer_safe<int16>("-1234567").is_error());
- ASSERT_TRUE(to_integer_safe<uint16>("-1234567").is_error());
- ASSERT_TRUE(to_integer_safe<int16>("-1254567").is_error());
- ASSERT_TRUE(to_integer_safe<uint16>("1254567").is_error());
- ASSERT_EQ(to_integer_safe<int64>("-12345678910111213").ok(), -12345678910111213);
- ASSERT_EQ(to_integer_safe<uint64>("12345678910111213").ok(), 12345678910111213ull);
- ASSERT_TRUE(to_integer_safe<uint64>("-12345678910111213").is_error());
-}
-
-static void test_to_double_one(CSlice str, Slice expected, int precision = 6) {
+ ASSERT_EQ(td::to_integer<td::int32>("-1234567"), -1234567);
+ ASSERT_EQ(td::to_integer<td::int64>("-1234567"), -1234567);
+ ASSERT_EQ(td::to_integer<td::uint32>("-1234567"), 0u);
+ ASSERT_EQ(td::to_integer<td::int16>("-1234567"), 10617);
+ ASSERT_EQ(td::to_integer<td::uint16>("-1234567"), 0u);
+ ASSERT_EQ(td::to_integer<td::int16>("-1254567"), -9383);
+ ASSERT_EQ(td::to_integer<td::uint16>("1254567"), 9383u);
+ ASSERT_EQ(td::to_integer<td::int64>("-12345678910111213"), -12345678910111213);
+ ASSERT_EQ(td::to_integer<td::uint64>("12345678910111213"), 12345678910111213ull);
+
+ ASSERT_EQ(td::to_integer_safe<td::int32>("-1234567").ok(), -1234567);
+ ASSERT_EQ(td::to_integer_safe<td::int64>("-1234567").ok(), -1234567);
+ ASSERT_TRUE(td::to_integer_safe<td::uint32>("-1234567").is_error());
+ ASSERT_TRUE(td::to_integer_safe<td::int16>("-1234567").is_error());
+ ASSERT_TRUE(td::to_integer_safe<td::uint16>("-1234567").is_error());
+ ASSERT_TRUE(td::to_integer_safe<td::int16>("-1254567").is_error());
+ ASSERT_TRUE(td::to_integer_safe<td::uint16>("1254567").is_error());
+ ASSERT_EQ(td::to_integer_safe<td::int64>("-12345678910111213").ok(), -12345678910111213);
+ ASSERT_EQ(td::to_integer_safe<td::uint64>("12345678910111213").ok(), 12345678910111213ull);
+ ASSERT_TRUE(td::to_integer_safe<td::uint64>("-12345678910111213").is_error());
+}
+
+static void test_to_double_one(td::CSlice str, td::Slice expected, int precision = 6) {
auto result = PSTRING() << td::StringBuilder::FixedDouble(to_double(str), precision);
if (expected != result) {
LOG(ERROR) << "To double conversion failed: have " << str << ", expected " << expected << ", parsed "
@@ -234,29 +477,786 @@ static void test_to_double() {
TEST(Misc, to_double) {
test_to_double();
- const char *locale_name = (std::setlocale(LC_ALL, "fr-FR") == nullptr ? "" : "fr-FR");
- std::locale new_locale(locale_name);
- std::locale::global(new_locale);
+ std::locale new_locale("C");
+ auto host_locale = std::locale::global(new_locale);
+ test_to_double();
+ new_locale = std::locale::global(std::locale::classic());
test_to_double();
- std::locale::global(std::locale::classic());
+ auto classic_locale = std::locale::global(host_locale);
test_to_double();
}
-static void test_get_url_query_file_name_one(const char *prefix, const char *suffix, const char *file_name) {
- auto path = string(prefix) + string(file_name) + string(suffix);
- ASSERT_STREQ(file_name, get_url_query_file_name(path));
- ASSERT_STREQ(file_name, get_url_file_name("http://telegram.org" + path));
- ASSERT_STREQ(file_name, get_url_file_name("http://telegram.org:80" + path));
- ASSERT_STREQ(file_name, get_url_file_name("telegram.org" + path));
+TEST(Misc, print_int) {
+ ASSERT_STREQ("-9223372036854775808", PSLICE() << -9223372036854775807 - 1);
+ ASSERT_STREQ("-2147483649", PSLICE() << -2147483649ll);
+ ASSERT_STREQ("-2147483648", PSLICE() << -2147483647 - 1);
+ ASSERT_STREQ("-2147483647", PSLICE() << -2147483647);
+ ASSERT_STREQ("-123456789", PSLICE() << -123456789);
+ ASSERT_STREQ("-1", PSLICE() << -1);
+ ASSERT_STREQ("0", PSLICE() << 0);
+ ASSERT_STREQ("1", PSLICE() << 1);
+ ASSERT_STREQ("9", PSLICE() << 9);
+ ASSERT_STREQ("10", PSLICE() << 10);
+ ASSERT_STREQ("2147483647", PSLICE() << 2147483647);
+ ASSERT_STREQ("2147483648", PSLICE() << 2147483648ll);
+ ASSERT_STREQ("2147483649", PSLICE() << 2147483649ll);
+ ASSERT_STREQ("9223372036854775807", PSLICE() << 9223372036854775807ll);
+}
+
+TEST(Misc, print_uint) {
+ ASSERT_STREQ("0", PSLICE() << 0u);
+ ASSERT_STREQ("1", PSLICE() << 1u);
+ ASSERT_STREQ("9", PSLICE() << 9u);
+ ASSERT_STREQ("10", PSLICE() << 10u);
+ ASSERT_STREQ("2147483647", PSLICE() << 2147483647u);
+ ASSERT_STREQ("2147483648", PSLICE() << 2147483648u);
+ ASSERT_STREQ("2147483649", PSLICE() << 2147483649u);
+ ASSERT_STREQ("9223372036854775807", PSLICE() << 9223372036854775807u);
+}
+
+static void test_idn_to_ascii_one(const td::string &host, const td::string &result) {
+ if (result != td::idn_to_ascii(host).ok()) {
+ LOG(ERROR) << "Failed to convert " << host << " to " << result << ", got \"" << td::idn_to_ascii(host).ok() << "\"";
+ }
+}
+
+TEST(Misc, idn_to_ascii) {
+ test_idn_to_ascii_one("::::::::::::::::::::::::::::::::::::::@/", "::::::::::::::::::::::::::::::::::::::@/");
+ test_idn_to_ascii_one("", "");
+ test_idn_to_ascii_one("%30", "%30");
+ test_idn_to_ascii_one("127.0.0.1", "127.0.0.1");
+ test_idn_to_ascii_one("fe80::", "fe80::");
+ test_idn_to_ascii_one("fe80:0:0:0:200:f8ff:fe21:67cf", "fe80:0:0:0:200:f8ff:fe21:67cf");
+ test_idn_to_ascii_one("2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d", "2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d");
+ test_idn_to_ascii_one("::ffff:192.0.2.1", "::ffff:192.0.2.1");
+ test_idn_to_ascii_one("ABCDEF", "abcdef");
+ test_idn_to_ascii_one("abcdef", "abcdef");
+ test_idn_to_ascii_one("abæcdöef", "xn--abcdef-qua4k");
+ test_idn_to_ascii_one("schön", "xn--schn-7qa");
+ test_idn_to_ascii_one("ยจฆฟคฏข", "xn--22cdfh1b8fsa");
+ test_idn_to_ascii_one("☺", "xn--74h");
+ test_idn_to_ascii_one("правда", "xn--80aafi6cg");
+ test_idn_to_ascii_one("büücher", "xn--bcher-kvaa");
+ test_idn_to_ascii_one("BüüCHER", "xn--bcher-kvaa");
+ test_idn_to_ascii_one("bücüher", "xn--bcher-kvab");
+ test_idn_to_ascii_one("bücherü", "xn--bcher-kvae");
+ test_idn_to_ascii_one("ýbücher", "xn--bcher-kvaf");
+ test_idn_to_ascii_one("übücher", "xn--bcher-jvab");
+ test_idn_to_ascii_one("bücher.tld", "xn--bcher-kva.tld");
+ test_idn_to_ascii_one("кто.рф", "xn--j1ail.xn--p1ai");
+ test_idn_to_ascii_one("wіkіреdіа.org", "xn--wkd-8cdx9d7hbd.org");
+ test_idn_to_ascii_one("cnwin2k8中国.avol.com", "xn--cnwin2k8-sd0mx14e.avol.com");
+ test_idn_to_ascii_one("win-2k12r2-addc.阿伯测阿伯测ad.hai.com", "win-2k12r2-addc.xn--ad-tl3ca3569aba8944eca.hai.com");
+ test_idn_to_ascii_one("✌.ws", "xn--7bi.ws");
+ // test_idn_to_ascii_one("✌️.ws", "xn--7bi.ws"); // needs nameprep to succeed
+ test_idn_to_ascii_one("⛧", "xn--59h");
+ test_idn_to_ascii_one("--рф.рф", "xn-----mmcq.xn--p1ai");
+ ASSERT_TRUE(td::idn_to_ascii("\xc0").is_error());
+}
+
+#if TD_WINDOWS
+static void test_to_wstring_one(const td::string &str) {
+ ASSERT_STREQ(str, td::from_wstring(td::to_wstring(str).ok()).ok());
+}
+
+TEST(Misc, to_wstring) {
+ test_to_wstring_one("");
+ for (int i = 0; i < 10; i++) {
+ test_to_wstring_one("test");
+ test_to_wstring_one("тест");
+ }
+ td::string str;
+ for (td::uint32 i = 0; i <= 0xD7FF; i++) {
+ td::append_utf8_character(str, i);
+ }
+ for (td::uint32 i = 0xE000; i <= 0x10FFFF; i++) {
+ td::append_utf8_character(str, i);
+ }
+ test_to_wstring_one(str);
+ ASSERT_TRUE(td::to_wstring("\xc0").is_error());
+ auto emoji = td::to_wstring("🏟").ok();
+ ASSERT_TRUE(td::from_wstring(emoji).ok() == "🏟");
+ ASSERT_TRUE(emoji.size() == 2);
+ auto emoji2 = emoji;
+ emoji[0] = emoji[1];
+ emoji2[1] = emoji2[0];
+ ASSERT_TRUE(td::from_wstring(emoji).is_error());
+ ASSERT_TRUE(td::from_wstring(emoji2).is_error());
+ emoji2[0] = emoji[0];
+ ASSERT_TRUE(td::from_wstring(emoji2).is_error());
+}
+#endif
+
+static void test_translit(const td::string &word, const td::vector<td::string> &result, bool allow_partial = true) {
+ ASSERT_EQ(result, td::get_word_transliterations(word, allow_partial));
+}
+
+TEST(Misc, translit) {
+ test_translit("word", {"word", "ворд"});
+ test_translit("", {});
+ test_translit("ььььььььь", {"ььььььььь"});
+ test_translit("крыло", {"krylo", "крыло"});
+ test_translit("krylo", {"krylo", "крило"});
+ test_translit("crylo", {"crylo", "крило"});
+ test_translit("cheiia", {"cheiia", "кхеииа", "чейия"});
+ test_translit("cheii", {"cheii", "кхеии", "чейи", "чейий", "чейия"});
+ test_translit("s", {"s", "с", "ш", "щ"});
+ test_translit("y", {"e", "y", "е", "и", "ю", "я"});
+ test_translit("j", {"e", "j", "е", "й", "ю", "я"});
+ test_translit("yo", {"e", "yo", "е", "ио"});
+ test_translit("artjom", {"artem", "artjom", "артем", "артйом"});
+ test_translit("artyom", {"artem", "artyom", "артем", "артиом"});
+ test_translit("arty", {"arte", "arty", "арте", "арти", "артю", "артя"});
+ test_translit("льи", {"li", "lia", "ly", "льи"});
+ test_translit("y", {"y", "и"}, false);
+ test_translit("yo", {"e", "yo", "е", "ио"}, false);
+}
+
+static void test_unicode(td::uint32 (*func)(td::uint32)) {
+ for (td::uint32 i = 0; i <= 0x110000; i++) {
+ auto res = func(i);
+ CHECK(res <= 0x10ffff);
+ }
+}
+
+TEST(Misc, unicode) {
+ test_unicode(td::prepare_search_character);
+ test_unicode(td::unicode_to_lower);
+ test_unicode(td::remove_diacritics);
+}
+
+TEST(Misc, get_unicode_simple_category) {
+ td::uint32 result = 0;
+ for (size_t t = 0; t < 100; t++) {
+ for (td::uint32 i = 0; i <= 0x10ffff; i++) {
+ result = result * 123 + static_cast<td::uint32>(static_cast<int>(td::get_unicode_simple_category(i)));
+ }
+ }
+ LOG(INFO) << result;
+}
+
+TEST(Misc, get_unicode_simple_category_small) {
+ td::uint32 result = 0;
+ for (size_t t = 0; t < 1000; t++) {
+ for (td::uint32 i = 0; i <= 0xffff; i++) {
+ result = result * 123 + static_cast<td::uint32>(static_cast<int>(td::get_unicode_simple_category(i)));
+ }
+ }
+ LOG(INFO) << result;
+}
+
+TEST(BigNum, from_decimal) {
+ ASSERT_TRUE(td::BigNum::from_decimal("").is_error());
+ ASSERT_TRUE(td::BigNum::from_decimal("a").is_error());
+ ASSERT_TRUE(td::BigNum::from_decimal("123a").is_error());
+ ASSERT_TRUE(td::BigNum::from_decimal("-123a").is_error());
+ // ASSERT_TRUE(td::BigNum::from_decimal("-").is_error());
+ ASSERT_TRUE(td::BigNum::from_decimal("123").is_ok());
+ ASSERT_TRUE(td::BigNum::from_decimal("-123").is_ok());
+ ASSERT_TRUE(td::BigNum::from_decimal("0").is_ok());
+ ASSERT_TRUE(td::BigNum::from_decimal("-0").is_ok());
+ ASSERT_TRUE(td::BigNum::from_decimal("-999999999999999999999999999999999999999999999999").is_ok());
+ ASSERT_TRUE(td::BigNum::from_decimal("999999999999999999999999999999999999999999999999").is_ok());
+}
+
+TEST(BigNum, from_binary) {
+ ASSERT_STREQ(td::BigNum::from_binary("").to_decimal(), "0");
+ ASSERT_STREQ(td::BigNum::from_binary("a").to_decimal(), "97");
+ ASSERT_STREQ(td::BigNum::from_binary("\x00\xff").to_decimal(), "255");
+ ASSERT_STREQ(td::BigNum::from_binary("\x00\x01\x00\x00").to_decimal(), "65536");
+ ASSERT_STREQ(td::BigNum::from_le_binary("").to_decimal(), "0");
+ ASSERT_STREQ(td::BigNum::from_le_binary("a").to_decimal(), "97");
+ ASSERT_STREQ(td::BigNum::from_le_binary("\x00\xff").to_decimal(), "65280");
+ ASSERT_STREQ(td::BigNum::from_le_binary("\x00\x01\x00\x00").to_decimal(), "256");
+ ASSERT_STREQ(td::BigNum::from_decimal("255").move_as_ok().to_binary(), "\xff");
+ ASSERT_STREQ(td::BigNum::from_decimal("255").move_as_ok().to_le_binary(), "\xff");
+ ASSERT_STREQ(td::BigNum::from_decimal("255").move_as_ok().to_binary(2), "\x00\xff");
+ ASSERT_STREQ(td::BigNum::from_decimal("255").move_as_ok().to_le_binary(2), "\xff\x00");
+ ASSERT_STREQ(td::BigNum::from_decimal("65280").move_as_ok().to_binary(), "\xff\x00");
+ ASSERT_STREQ(td::BigNum::from_decimal("65280").move_as_ok().to_le_binary(), "\x00\xff");
+ ASSERT_STREQ(td::BigNum::from_decimal("65280").move_as_ok().to_binary(2), "\xff\x00");
+ ASSERT_STREQ(td::BigNum::from_decimal("65280").move_as_ok().to_le_binary(2), "\x00\xff");
+ ASSERT_STREQ(td::BigNum::from_decimal("65536").move_as_ok().to_binary(), "\x01\x00\x00");
+ ASSERT_STREQ(td::BigNum::from_decimal("65536").move_as_ok().to_le_binary(), "\x00\x00\x01");
+ ASSERT_STREQ(td::BigNum::from_decimal("65536").move_as_ok().to_binary(4), "\x00\x01\x00\x00");
+ ASSERT_STREQ(td::BigNum::from_decimal("65536").move_as_ok().to_le_binary(4), "\x00\x00\x01\x00");
+}
+
+static void test_get_ipv4(td::uint32 ip) {
+ td::IPAddress ip_address;
+ ip_address.init_ipv4_port(td::IPAddress::ipv4_to_str(ip), 80).ensure();
+ ASSERT_EQ(ip_address.get_ipv4(), ip);
+}
+
+TEST(Misc, IPAddress_get_ipv4) {
+ test_get_ipv4(0x00000000);
+ test_get_ipv4(0x010000FF);
+ test_get_ipv4(0xFF000001);
+ test_get_ipv4(0x01020304);
+ test_get_ipv4(0x04030201);
+ test_get_ipv4(0xFFFFFFFF);
+}
+
+static void test_is_reserved(const td::string &ip, bool is_reserved) {
+ td::IPAddress ip_address;
+ ip_address.init_ipv4_port(ip, 80).ensure();
+ ASSERT_EQ(is_reserved, ip_address.is_reserved());
+}
+
+TEST(Misc, IPAddress_is_reserved) {
+ test_is_reserved("0.0.0.0", true);
+ test_is_reserved("0.255.255.255", true);
+ test_is_reserved("1.0.0.0", false);
+ test_is_reserved("5.0.0.0", false);
+ test_is_reserved("9.255.255.255", false);
+ test_is_reserved("10.0.0.0", true);
+ test_is_reserved("10.255.255.255", true);
+ test_is_reserved("11.0.0.0", false);
+ test_is_reserved("100.63.255.255", false);
+ test_is_reserved("100.64.0.0", true);
+ test_is_reserved("100.127.255.255", true);
+ test_is_reserved("100.128.0.0", false);
+ test_is_reserved("126.255.255.255", false);
+ test_is_reserved("127.0.0.0", true);
+ test_is_reserved("127.255.255.255", true);
+ test_is_reserved("128.0.0.0", false);
+ test_is_reserved("169.253.255.255", false);
+ test_is_reserved("169.254.0.0", true);
+ test_is_reserved("169.254.255.255", true);
+ test_is_reserved("169.255.0.0", false);
+ test_is_reserved("172.15.255.255", false);
+ test_is_reserved("172.16.0.0", true);
+ test_is_reserved("172.31.255.255", true);
+ test_is_reserved("172.32.0.0", false);
+ test_is_reserved("191.255.255.255", false);
+ test_is_reserved("192.0.0.0", true);
+ test_is_reserved("192.0.0.255", true);
+ test_is_reserved("192.0.1.0", false);
+ test_is_reserved("192.0.1.255", false);
+ test_is_reserved("192.0.2.0", true);
+ test_is_reserved("192.0.2.255", true);
+ test_is_reserved("192.0.3.0", false);
+ test_is_reserved("192.88.98.255", false);
+ test_is_reserved("192.88.99.0", true);
+ test_is_reserved("192.88.99.255", true);
+ test_is_reserved("192.88.100.0", false);
+ test_is_reserved("192.167.255.255", false);
+ test_is_reserved("192.168.0.0", true);
+ test_is_reserved("192.168.255.255", true);
+ test_is_reserved("192.169.0.0", false);
+ test_is_reserved("198.17.255.255", false);
+ test_is_reserved("198.18.0.0", true);
+ test_is_reserved("198.19.255.255", true);
+ test_is_reserved("198.20.0.0", false);
+ test_is_reserved("198.51.99.255", false);
+ test_is_reserved("198.51.100.0", true);
+ test_is_reserved("198.51.100.255", true);
+ test_is_reserved("198.51.101.0", false);
+ test_is_reserved("203.0.112.255", false);
+ test_is_reserved("203.0.113.0", true);
+ test_is_reserved("203.0.113.255", true);
+ test_is_reserved("203.0.114.0", false);
+ test_is_reserved("223.255.255.255", false);
+ test_is_reserved("224.0.0.0", true);
+ test_is_reserved("239.255.255.255", true);
+ test_is_reserved("240.0.0.0", true);
+ test_is_reserved("255.255.255.254", true);
+ test_is_reserved("255.255.255.255", true);
+}
+
+TEST(Misc, ipv6_clear) {
+ td::IPAddress ip_address;
+ ip_address.init_host_port("2001:0db8:85a3:0000:0000:8a2e:0370:7334", 123).ensure();
+ ASSERT_EQ("2001:db8:85a3::8a2e:370:7334", ip_address.get_ip_str());
+ ip_address.clear_ipv6_interface();
+ ASSERT_EQ("2001:db8:85a3::", ip_address.get_ip_str());
+}
+
+static void test_split(td::Slice str, std::pair<td::Slice, td::Slice> expected) {
+ ASSERT_EQ(expected, td::split(str));
+}
+
+TEST(Misc, split) {
+ test_split("", {"", ""});
+ test_split(" ", {"", ""});
+ test_split("abcdef", {"abcdef", ""});
+ test_split("abc def", {"abc", "def"});
+ test_split("a bcdef", {"a", "bcdef"});
+ test_split(" abcdef", {"", "abcdef"});
+ test_split("abcdef ", {"abcdef", ""});
+ test_split("ab cd ef", {"ab", "cd ef"});
+ test_split("ab cdef ", {"ab", "cdef "});
+ test_split(" abcd ef", {"", "abcd ef"});
+ test_split(" abcdef ", {"", "abcdef "});
}
-TEST(Misc, get_url_query_file_name) {
- for (auto suffix : {"?t=1#test", "#test?t=1", "#?t=1", "?t=1#", "#test", "?t=1", "#", "?", ""}) {
- test_get_url_query_file_name_one("", suffix, "");
- test_get_url_query_file_name_one("/", suffix, "");
- test_get_url_query_file_name_one("/a/adasd/", suffix, "");
- test_get_url_query_file_name_one("/a/lklrjetn/", suffix, "adasd.asdas");
- test_get_url_query_file_name_one("/", suffix, "a123asadas");
- test_get_url_query_file_name_one("/", suffix, "\\a\\1\\2\\3\\a\\s\\a\\das");
+static void test_full_split(td::Slice str, const td::vector<td::Slice> &expected) {
+ ASSERT_EQ(expected, td::full_split(str));
+}
+
+static void test_full_split(td::Slice str, char c, std::size_t max_parts, const td::vector<td::Slice> &expected) {
+ ASSERT_EQ(expected, td::full_split(str, c, max_parts));
+}
+
+TEST(Misc, full_split) {
+ test_full_split("", {});
+ test_full_split(" ", {"", ""});
+ test_full_split(" ", {"", "", ""});
+ test_full_split("abcdef", {"abcdef"});
+ test_full_split("abc def", {"abc", "def"});
+ test_full_split("a bcdef", {"a", "bcdef"});
+ test_full_split(" abcdef", {"", "abcdef"});
+ test_full_split("abcdef ", {"abcdef", ""});
+ test_full_split("ab cd ef", {"ab", "cd", "ef"});
+ test_full_split("ab cdef ", {"ab", "cdef", ""});
+ test_full_split(" abcd ef", {"", "abcd", "ef"});
+ test_full_split(" abcdef ", {"", "abcdef", ""});
+ test_full_split(" ab cd ef ", {"", "ab", "cd", "ef", ""});
+ test_full_split(" ab cd ef ", {"", "", "ab", "", "cd", "", "ef", "", ""});
+ test_full_split("ab cd ef gh", ' ', 3, {"ab", "cd", "ef gh"});
+}
+
+TEST(Misc, StringBuilder) {
+ auto small_str = td::string{"abcdefghij"};
+ auto big_str = td::string(1000, 'a');
+ using V = td::vector<td::string>;
+ for (auto use_buf : {false, true}) {
+ for (std::size_t initial_buffer_size : {0, 1, 5, 10, 100, 1000, 2000}) {
+ for (const auto &test :
+ {V{small_str}, V{small_str, big_str, big_str, small_str}, V{big_str, small_str, big_str}}) {
+ td::string buf(initial_buffer_size, '\0');
+ td::StringBuilder sb(buf, use_buf);
+ td::string res;
+ for (const auto &x : test) {
+ res += x;
+ sb << x;
+ }
+ if (use_buf) {
+ ASSERT_EQ(res, sb.as_cslice());
+ } else {
+ auto got = sb.as_cslice();
+ res.resize(got.size());
+ ASSERT_EQ(res, got);
+ }
+ }
+ }
+ }
+}
+
+TEST(Misc, As) {
+ char buf[100];
+ td::as<int>(buf) = 123;
+ ASSERT_EQ(123, td::as<int>(static_cast<const char *>(buf)));
+ ASSERT_EQ(123, td::as<int>(static_cast<char *>(buf)));
+ char buf2[100];
+ td::as<int>(buf2) = td::as<int>(buf);
+ ASSERT_EQ(123, td::as<int>(static_cast<const char *>(buf2)));
+ ASSERT_EQ(123, td::as<int>(static_cast<char *>(buf2)));
+}
+
+TEST(Misc, Regression) {
+ td::string name = "regression_db";
+ td::RegressionTester::destroy(name);
+
+ {
+ auto tester = td::RegressionTester::create(name);
+ tester->save_db();
+ tester->verify_test("one_plus_one", "two").ensure();
+ tester->verify_test("one_plus_one", "two").ensure();
+ tester->verify_test("two_plus_one", "three").ensure();
+ tester->verify_test("one_plus_one", "two").ensure();
+ tester->verify_test("two_plus_one", "three").ensure();
+ tester->save_db();
+ }
+ {
+ auto tester = td::RegressionTester::create(name);
+ tester->save_db();
+ tester->verify_test("one_plus_one", "two").ensure();
+ tester->verify_test("one_plus_one", "two").ensure();
+ tester->verify_test("two_plus_one", "three").ensure();
+ tester->verify_test("one_plus_one", "two").ensure();
+ tester->verify_test("two_plus_one", "three").ensure();
+ tester->save_db();
+ tester->verify_test("one_plus_one", "three").ensure_error();
+ tester->verify_test("two_plus_one", "two").ensure_error();
+ }
+ {
+ auto tester = td::RegressionTester::create(name);
+ tester->verify_test("one_plus_one", "three").ensure_error();
+ tester->verify_test("two_plus_one", "two").ensure_error();
+ }
+}
+
+TEST(Misc, Bits) {
+ ASSERT_EQ(32, td::count_leading_zeroes32(0));
+ ASSERT_EQ(64, td::count_leading_zeroes64(0));
+ ASSERT_EQ(32, td::count_trailing_zeroes32(0));
+ ASSERT_EQ(64, td::count_trailing_zeroes64(0));
+
+ for (int i = 0; i < 32; i++) {
+ ASSERT_EQ(31 - i, td::count_leading_zeroes32(1u << i));
+ ASSERT_EQ(i, td::count_trailing_zeroes32(1u << i));
+ ASSERT_EQ(31 - i, td::count_leading_zeroes_non_zero32(1u << i));
+ ASSERT_EQ(i, td::count_trailing_zeroes_non_zero32(1u << i));
+ }
+ for (int i = 0; i < 64; i++) {
+ ASSERT_EQ(63 - i, td::count_leading_zeroes64(1ull << i));
+ ASSERT_EQ(i, td::count_trailing_zeroes64(1ull << i));
+ ASSERT_EQ(63 - i, td::count_leading_zeroes_non_zero64(1ull << i));
+ ASSERT_EQ(i, td::count_trailing_zeroes_non_zero64(1ull << i));
+ }
+
+ ASSERT_EQ(0x12345678u, td::bswap32(0x78563412u));
+ ASSERT_EQ(0x12345678abcdef67ull, td::bswap64(0x67efcdab78563412ull));
+
+ td::uint8 buf[8] = {1, 90, 2, 18, 129, 255, 0, 2};
+ td::uint64 num2 = td::bswap64(td::as<td::uint64>(buf));
+ td::uint64 num = (static_cast<td::uint64>(buf[0]) << 56) | (static_cast<td::uint64>(buf[1]) << 48) |
+ (static_cast<td::uint64>(buf[2]) << 40) | (static_cast<td::uint64>(buf[3]) << 32) |
+ (static_cast<td::uint64>(buf[4]) << 24) | (static_cast<td::uint64>(buf[5]) << 16) |
+ (static_cast<td::uint64>(buf[6]) << 8) | (static_cast<td::uint64>(buf[7]));
+ ASSERT_EQ(num, num2);
+
+ ASSERT_EQ(0, td::count_bits32(0));
+ ASSERT_EQ(0, td::count_bits64(0));
+ ASSERT_EQ(4, td::count_bits32((1u << 31) | 7));
+ ASSERT_EQ(4, td::count_bits64((1ull << 63) | 7));
+}
+
+TEST(Misc, BitsRange) {
+ auto to_vec_a = [](td::uint64 x) {
+ td::vector<td::int32> bits;
+ for (auto i : td::BitsRange(x)) {
+ bits.push_back(i);
+ }
+ return bits;
+ };
+
+ auto to_vec_b = [](td::uint64 x) {
+ td::vector<td::int32> bits;
+ td::int32 pos = 0;
+ while (x != 0) {
+ if ((x & 1) != 0) {
+ bits.push_back(pos);
+ }
+ x >>= 1;
+ pos++;
+ }
+ return bits;
+ };
+
+ auto do_check = [](const td::vector<td::int32> &a, const td::vector<td::int32> &b) {
+ ASSERT_EQ(b, a);
+ };
+ auto check = [&](td::uint64 x) {
+ do_check(to_vec_a(x), to_vec_b(x));
+ };
+
+ do_check(to_vec_a(21), {0, 2, 4});
+ for (int x = 0; x < 100; x++) {
+ check(x);
+ check(std::numeric_limits<td::uint32>::max() - x);
+ check(std::numeric_limits<td::uint64>::max() - x);
+ }
+}
+
+#if !TD_THREAD_UNSUPPORTED
+TEST(Misc, Time) {
+ td::Stage run;
+ td::Stage check;
+ td::Stage finish;
+
+ std::size_t threads_n = 3;
+ td::vector<td::thread> threads;
+ td::vector<std::atomic<double>> ts(threads_n);
+ for (std::size_t i = 0; i < threads_n; i++) {
+ threads.emplace_back([&, thread_id = i] {
+ for (td::uint64 round = 1; round < 10000; round++) {
+ ts[thread_id] = 0;
+ run.wait(round * threads_n);
+ ts[thread_id] = td::Time::now();
+ check.wait(round * threads_n);
+ for (auto &ts_ref : ts) {
+ auto other_ts = ts_ref.load();
+ if (other_ts != 0) {
+ ASSERT_TRUE(other_ts <= td::Time::now_cached());
+ }
+ }
+
+ finish.wait(round * threads_n);
+ }
+ });
+ }
+ for (auto &thread : threads) {
+ thread.join();
+ }
+}
+#endif
+
+TEST(Misc, uint128) {
+ td::vector<td::uint64> parts = {0,
+ 1,
+ 2000,
+ 2001,
+ std::numeric_limits<td::uint64>::max(),
+ std::numeric_limits<td::uint64>::max() - 1,
+ std::numeric_limits<td::uint32>::max(),
+ static_cast<td::uint64>(std::numeric_limits<td::uint32>::max()) + 1};
+ td::vector<td::int64> signed_parts = {0,
+ 1,
+ 2000,
+ 2001,
+ -1,
+ -2000,
+ -2001,
+ std::numeric_limits<td::int64>::max(),
+ std::numeric_limits<td::int64>::max() - 1,
+ std::numeric_limits<td::int64>::min(),
+ std::numeric_limits<td::int64>::min() + 1,
+ std::numeric_limits<td::int32>::max(),
+ static_cast<td::int64>(std::numeric_limits<td::int32>::max()) + 1,
+ std::numeric_limits<td::int32>::max() - 1,
+ std::numeric_limits<td::int32>::min(),
+ std::numeric_limits<td::int32>::min() + 1,
+ static_cast<td::int64>(std::numeric_limits<td::int32>::min()) - 1};
+
+#if TD_HAVE_INT128
+ auto to_intrinsic = [](td::uint128_emulated num) {
+ return td::uint128_intrinsic(num.hi(), num.lo());
+ };
+ auto eq = [](td::uint128_emulated a, td::uint128_intrinsic b) {
+ return a.hi() == b.hi() && a.lo() == b.lo();
+ };
+ auto ensure_eq = [&](td::uint128_emulated a, td::uint128_intrinsic b) {
+ if (!eq(a, b)) {
+ LOG(FATAL) << "[" << a.hi() << ";" << a.lo() << "] vs [" << b.hi() << ";" << b.lo() << "]";
+ }
+ };
+#endif
+
+ td::vector<td::uint128_emulated> nums;
+ for (auto hi : parts) {
+ for (auto lo : parts) {
+ auto a = td::uint128_emulated(hi, lo);
+#if TD_HAVE_INT128
+ auto ia = td::uint128_intrinsic(hi, lo);
+ ensure_eq(a, ia);
+#endif
+ nums.push_back(a);
+ nums.pop_back();
+ nums.push_back({hi, lo});
+ }
+ }
+
+#if TD_HAVE_INT128
+ for (auto a : nums) {
+ auto ia = to_intrinsic(a);
+ ensure_eq(a, ia);
+ CHECK(a.is_zero() == ia.is_zero());
+ for (int i = 0; i <= 130; i++) {
+ ensure_eq(a.shl(i), ia.shl(i));
+ ensure_eq(a.shr(i), ia.shr(i));
+ }
+ for (auto b : parts) {
+ ensure_eq(a.mult(b), ia.mult(b));
+ }
+ for (auto b : signed_parts) {
+ ensure_eq(a.mult_signed(b), ia.mult_signed(b));
+ if (b == 0) {
+ continue;
+ }
+ td::int64 q;
+ td::int64 r;
+ a.divmod_signed(b, &q, &r);
+ td::int64 iq;
+ td::int64 ir;
+ ia.divmod_signed(b, &iq, &ir);
+ ASSERT_EQ(q, iq);
+ ASSERT_EQ(r, ir);
+ }
+ for (auto b : nums) {
+ auto ib = to_intrinsic(b);
+ //LOG(ERROR) << ia.hi() << ";" << ia.lo() << " " << ib.hi() << ";" << ib.lo();
+ ensure_eq(a.mult(b), ia.mult(ib));
+ ensure_eq(a.add(b), ia.add(ib));
+ ensure_eq(a.sub(b), ia.sub(ib));
+ if (!b.is_zero()) {
+ ensure_eq(a.div(b), ia.div(ib));
+ ensure_eq(a.mod(b), ia.mod(ib));
+ }
+ }
+ }
+
+ for (auto signed_part : signed_parts) {
+ auto a = td::uint128_emulated::from_signed(signed_part);
+ auto ia = td::uint128_intrinsic::from_signed(signed_part);
+ ensure_eq(a, ia);
+ }
+#endif
+}
+
+template <template <class T> class HashT, class ValueT>
+static td::Status test_hash(const td::vector<ValueT> &values) {
+ for (std::size_t i = 0; i < values.size(); i++) {
+ for (std::size_t j = i; j < values.size(); j++) {
+ auto &a = values[i];
+ auto &b = values[j];
+ auto a_hash = HashT<ValueT>()(a);
+ auto b_hash = HashT<ValueT>()(b);
+ if (a == b) {
+ if (a_hash != b_hash) {
+ return td::Status::Error("Hash differs for same values");
+ }
+ } else {
+ if (a_hash == b_hash) {
+ return td::Status::Error("Hash is the same for different values");
+ }
+ }
+ }
+ }
+ return td::Status::OK();
+}
+
+class BadValue {
+ public:
+ explicit BadValue(std::size_t value) : value_(value) {
+ }
+
+ template <class H>
+ friend H AbslHashValue(H hasher, const BadValue &value) {
+ return hasher;
+ }
+ bool operator==(const BadValue &other) const {
+ return value_ == other.value_;
+ }
+
+ private:
+ std::size_t value_;
+};
+
+class ValueA {
+ public:
+ explicit ValueA(std::size_t value) : value_(value) {
+ }
+ template <class H>
+ friend H AbslHashValue(H hasher, ValueA value) {
+ return H::combine(std::move(hasher), value.value_);
+ }
+ bool operator==(const ValueA &other) const {
+ return value_ == other.value_;
+ }
+
+ private:
+ std::size_t value_;
+};
+
+class ValueB {
+ public:
+ explicit ValueB(std::size_t value) : value_(value) {
+ }
+
+ template <class H>
+ friend H AbslHashValue(H hasher, ValueB value) {
+ return H::combine(std::move(hasher), value.value_);
+ }
+ bool operator==(const ValueB &other) const {
+ return value_ == other.value_;
+ }
+
+ private:
+ std::size_t value_;
+};
+
+template <template <class T> class HashT>
+static void test_hash() {
+ // Just check that the following compiles
+ AbslHashValue(td::Hasher(), ValueA{1});
+ HashT<ValueA>()(ValueA{1});
+ std::unordered_map<ValueA, int, HashT<ValueA>> s;
+ s[ValueA{1}] = 1;
+ td::HashMap<ValueA, int> su;
+ su[ValueA{1}] = 1;
+ td::HashSet<ValueA> su2;
+ su2.insert(ValueA{1});
+#if TD_HAVE_ABSL
+ std::unordered_map<ValueA, int, absl::Hash<ValueA>> x;
+ absl::flat_hash_map<ValueA, int, HashT<ValueA>> sa;
+ sa[ValueA{1}] = 1;
+#endif
+
+ test_hash<HashT, std::size_t>({1, 2, 3, 4, 5}).ensure();
+ test_hash<HashT, BadValue>({BadValue{1}, BadValue{2}}).ensure_error();
+ test_hash<HashT, ValueA>({ValueA{1}, ValueA{2}}).ensure();
+ test_hash<HashT, ValueB>({ValueB{1}, ValueB{2}}).ensure();
+ test_hash<HashT, std::pair<int, int>>({{1, 1}, {1, 2}}).ensure();
+ // FIXME: use some better hash
+ //test_hash<HashT, std::pair<int, int>>({{1, 1}, {1, 2}, {2, 1}, {2, 2}}).ensure();
+}
+
+TEST(Misc, Hasher) {
+ test_hash<td::TdHash>();
+#if TD_HAVE_ABSL
+ test_hash<td::AbslHash>();
+#endif
+}
+
+TEST(Misc, CancellationToken) {
+ td::CancellationTokenSource source;
+ source.cancel();
+ auto token1 = source.get_cancellation_token();
+ auto token2 = source.get_cancellation_token();
+ CHECK(!token1);
+ source.cancel();
+ CHECK(token1);
+ CHECK(token2);
+ auto token3 = source.get_cancellation_token();
+ CHECK(!token3);
+ source.cancel();
+ CHECK(token3);
+
+ auto token4 = source.get_cancellation_token();
+ CHECK(!token4);
+ source = td::CancellationTokenSource{};
+ CHECK(token4);
+}
+
+TEST(Misc, Xorshift128plus) {
+ td::Random::Xorshift128plus rnd(123);
+ ASSERT_EQ(11453256657207062272ull, rnd());
+ ASSERT_EQ(14917490455889357332ull, rnd());
+ ASSERT_EQ(5645917797309401285ull, rnd());
+ ASSERT_EQ(13554822455746959330ull, rnd());
+}
+
+TEST(Misc, uname) {
+ auto first_version = td::get_operating_system_version();
+ auto second_version = td::get_operating_system_version();
+ ASSERT_STREQ(first_version, second_version);
+ ASSERT_EQ(first_version.begin(), second_version.begin());
+ ASSERT_TRUE(!first_version.empty());
+}
+
+TEST(Misc, serialize) {
+ td::int32 x = 1;
+ ASSERT_EQ(td::base64_encode(td::serialize(x)), td::base64_encode(td::string("\x01\x00\x00\x00", 4)));
+ td::int64 y = -2;
+ ASSERT_EQ(td::base64_encode(td::serialize(y)), td::base64_encode(td::string("\xfe\xff\xff\xff\xff\xff\xff\xff", 8)));
+}
+
+TEST(Misc, check_reset_guard) {
+ CheckExitGuard check_exit_guard{false};
+}
+
+TEST(FloodControl, Fast) {
+ td::FloodControlFast fc;
+ fc.add_limit(1, 5);
+ fc.add_limit(5, 10);
+
+ td::int32 count = 0;
+ double now = 0;
+ for (int i = 0; i < 100; i++) {
+ now = fc.get_wakeup_at();
+ fc.add_event(now);
+ LOG(INFO) << ++count << ": " << now;
}
}
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/port.cpp b/protocols/Telegram/tdlib/td/tdutils/test/port.cpp
new file mode 100644
index 0000000000..92b1729977
--- /dev/null
+++ b/protocols/Telegram/tdlib/td/tdutils/test/port.cpp
@@ -0,0 +1,316 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/algorithm.h"
+#include "td/utils/common.h"
+#include "td/utils/logging.h"
+#include "td/utils/misc.h"
+#include "td/utils/port/EventFd.h"
+#include "td/utils/port/FileFd.h"
+#include "td/utils/port/IoSlice.h"
+#include "td/utils/port/path.h"
+#include "td/utils/port/signals.h"
+#include "td/utils/port/sleep.h"
+#include "td/utils/port/thread.h"
+#include "td/utils/port/thread_local.h"
+#include "td/utils/Random.h"
+#include "td/utils/ScopeGuard.h"
+#include "td/utils/Slice.h"
+#include "td/utils/SliceBuilder.h"
+#include "td/utils/tests.h"
+#include "td/utils/Time.h"
+
+#if TD_PORT_POSIX && !TD_THREAD_UNSUPPORTED
+#include <algorithm>
+#include <atomic>
+#include <mutex>
+
+#include <pthread.h>
+#include <signal.h>
+#endif
+
+TEST(Port, files) {
+ td::CSlice main_dir = "test_dir";
+ td::rmrf(main_dir).ignore();
+ ASSERT_TRUE(td::FileFd::open(main_dir, td::FileFd::Write).is_error());
+ ASSERT_TRUE(td::walk_path(main_dir, [](td::CSlice name, td::WalkPath::Type type) { UNREACHABLE(); }).is_error());
+ td::mkdir(main_dir).ensure();
+ td::mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "A").ensure();
+ td::mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B").ensure();
+ td::mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "B" << TD_DIR_SLASH << "D").ensure();
+ td::mkdir(PSLICE() << main_dir << TD_DIR_SLASH << "C").ensure();
+ ASSERT_TRUE(td::FileFd::open(main_dir, td::FileFd::Write).is_error());
+ td::string fd_path = PSTRING() << main_dir << TD_DIR_SLASH << "t.txt";
+ td::string fd2_path = PSTRING() << main_dir << TD_DIR_SLASH << "C" << TD_DIR_SLASH << "t2.txt";
+
+ auto fd = td::FileFd::open(fd_path, td::FileFd::Write | td::FileFd::CreateNew).move_as_ok();
+ auto fd2 = td::FileFd::open(fd2_path, td::FileFd::Write | td::FileFd::CreateNew).move_as_ok();
+ fd2.close();
+
+ int cnt = 0;
+ const int ITER_COUNT = 1000;
+ for (int i = 0; i < ITER_COUNT; i++) {
+ td::walk_path(main_dir, [&](td::CSlice name, td::WalkPath::Type type) {
+ if (type == td::WalkPath::Type::NotDir) {
+ ASSERT_TRUE(name == fd_path || name == fd2_path);
+ }
+ cnt++;
+ }).ensure();
+ }
+ ASSERT_EQ((5 * 2 + 2) * ITER_COUNT, cnt);
+ bool was_abort = false;
+ td::walk_path(main_dir, [&](td::CSlice name, td::WalkPath::Type type) {
+ CHECK(!was_abort);
+ if (type == td::WalkPath::Type::EnterDir && ends_with(name, PSLICE() << TD_DIR_SLASH << "B")) {
+ was_abort = true;
+ return td::WalkPath::Action::Abort;
+ }
+ return td::WalkPath::Action::Continue;
+ }).ensure();
+ CHECK(was_abort);
+
+ cnt = 0;
+ bool is_first_dir = true;
+ td::walk_path(main_dir, [&](td::CSlice name, td::WalkPath::Type type) {
+ cnt++;
+ if (type == td::WalkPath::Type::EnterDir) {
+ if (is_first_dir) {
+ is_first_dir = false;
+ } else {
+ return td::WalkPath::Action::SkipDir;
+ }
+ }
+ return td::WalkPath::Action::Continue;
+ }).ensure();
+ ASSERT_EQ(6, cnt);
+
+ ASSERT_EQ(0u, fd.get_size().move_as_ok());
+ ASSERT_EQ(12u, fd.write("Hello world!").move_as_ok());
+ ASSERT_EQ(4u, fd.pwrite("abcd", 1).move_as_ok());
+ char buf[100];
+ td::MutableSlice buf_slice(buf, sizeof(buf));
+ ASSERT_TRUE(fd.pread(buf_slice.substr(0, 4), 2).is_error());
+ fd.seek(11).ensure();
+ ASSERT_EQ(2u, fd.write("?!").move_as_ok());
+
+ ASSERT_TRUE(td::FileFd::open(main_dir, td::FileFd::Read | td::FileFd::CreateNew).is_error());
+ fd = td::FileFd::open(fd_path, td::FileFd::Read | td::FileFd::Create).move_as_ok();
+ ASSERT_EQ(13u, fd.get_size().move_as_ok());
+ ASSERT_EQ(4u, fd.pread(buf_slice.substr(0, 4), 1).move_as_ok());
+ ASSERT_STREQ("abcd", buf_slice.substr(0, 4));
+
+ fd.seek(0).ensure();
+ ASSERT_EQ(13u, fd.read(buf_slice.substr(0, 13)).move_as_ok());
+ ASSERT_STREQ("Habcd world?!", buf_slice.substr(0, 13));
+}
+
+TEST(Port, SparseFiles) {
+ td::CSlice path = "sparse.txt";
+ td::unlink(path).ignore();
+ auto fd = td::FileFd::open(path, td::FileFd::Write | td::FileFd::CreateNew).move_as_ok();
+ ASSERT_EQ(0, fd.get_size().move_as_ok());
+ td::int64 offset = 100000000;
+ fd.pwrite("a", offset).ensure();
+ ASSERT_EQ(offset + 1, fd.get_size().move_as_ok());
+ auto real_size = fd.get_real_size().move_as_ok();
+ if (real_size >= offset + 1) {
+ LOG(ERROR) << "File system doesn't support sparse files, rewind during streaming can be slow";
+ }
+ td::unlink(path).ensure();
+}
+
+TEST(Port, LargeFiles) {
+ td::CSlice path = "large.txt";
+ td::unlink(path).ignore();
+ auto fd = td::FileFd::open(path, td::FileFd::Write | td::FileFd::CreateNew).move_as_ok();
+ ASSERT_EQ(0, fd.get_size().move_as_ok());
+ td::int64 offset = static_cast<td::int64>(3) << 30;
+ if (fd.pwrite("abcd", offset).is_error()) {
+ LOG(ERROR) << "Writing to large files isn't supported";
+ td::unlink(path).ensure();
+ return;
+ }
+ fd = td::FileFd::open(path, td::FileFd::Read).move_as_ok();
+ ASSERT_EQ(offset + 4, fd.get_size().move_as_ok());
+ td::string res(4, '\0');
+ if (fd.pread(res, offset).is_error()) {
+ LOG(ERROR) << "Reading of large files isn't supported";
+ td::unlink(path).ensure();
+ return;
+ }
+ ASSERT_STREQ(res, "abcd");
+ fd.close();
+ td::unlink(path).ensure();
+}
+
+TEST(Port, Writev) {
+ td::vector<td::IoSlice> vec;
+ td::CSlice test_file_path = "test.txt";
+ td::unlink(test_file_path).ignore();
+ auto fd = td::FileFd::open(test_file_path, td::FileFd::Write | td::FileFd::CreateNew).move_as_ok();
+ vec.push_back(td::as_io_slice("a"));
+ vec.push_back(td::as_io_slice("b"));
+ vec.push_back(td::as_io_slice("cd"));
+ ASSERT_EQ(4u, fd.writev(vec).move_as_ok());
+ vec.clear();
+ vec.push_back(td::as_io_slice("efg"));
+ vec.push_back(td::as_io_slice(""));
+ vec.push_back(td::as_io_slice("hi"));
+ ASSERT_EQ(5u, fd.writev(vec).move_as_ok());
+ fd.close();
+ fd = td::FileFd::open(test_file_path, td::FileFd::Read).move_as_ok();
+ td::Slice expected_content = "abcdefghi";
+ ASSERT_EQ(static_cast<td::int64>(expected_content.size()), fd.get_size().ok());
+ td::string content(expected_content.size(), '\0');
+ ASSERT_EQ(content.size(), fd.read(content).move_as_ok());
+ ASSERT_EQ(expected_content, content);
+}
+
+#if TD_PORT_POSIX && !TD_THREAD_UNSUPPORTED
+
+static std::mutex m;
+static td::vector<td::string> ptrs;
+static td::vector<int *> addrs;
+static TD_THREAD_LOCAL int thread_id;
+
+static void on_user_signal(int sig) {
+ int addr;
+ addrs[thread_id] = &addr;
+ std::unique_lock<std::mutex> guard(m);
+ ptrs.push_back(td::to_string(thread_id));
+}
+
+TEST(Port, SignalsAndThread) {
+ td::setup_signals_alt_stack().ensure();
+ td::set_signal_handler(td::SignalType::User, on_user_signal).ensure();
+ SCOPE_EXIT {
+ td::set_signal_handler(td::SignalType::User, nullptr).ensure();
+ };
+ td::vector<td::string> ans = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
+ {
+ td::vector<td::thread> threads;
+ int thread_n = 10;
+ td::vector<td::Stage> stages(thread_n);
+ ptrs.clear();
+ addrs.resize(thread_n);
+ for (int i = 0; i < 10; i++) {
+ threads.emplace_back([&, i] {
+ td::setup_signals_alt_stack().ensure();
+ if (i != 0) {
+ stages[i].wait(2);
+ }
+ thread_id = i;
+ pthread_kill(pthread_self(), SIGUSR1);
+ if (i + 1 < thread_n) {
+ stages[i + 1].wait(2);
+ }
+ });
+ }
+ for (auto &t : threads) {
+ t.join();
+ }
+ CHECK(ptrs == ans);
+
+ //LOG(ERROR) << ptrs;
+ //LOG(ERROR) << addrs;
+ }
+
+ {
+ td::Stage stage;
+ td::vector<td::thread> threads;
+ int thread_n = 10;
+ ptrs.clear();
+ addrs.resize(thread_n);
+ for (int i = 0; i < 10; i++) {
+ threads.emplace_back([&, i] {
+ stage.wait(thread_n);
+ thread_id = i;
+ pthread_kill(pthread_self(), SIGUSR1);
+ });
+ }
+ for (auto &t : threads) {
+ t.join();
+ }
+ std::sort(ptrs.begin(), ptrs.end());
+ CHECK(ptrs == ans);
+ auto addrs_size = addrs.size();
+ td::unique(addrs);
+ ASSERT_EQ(addrs_size, addrs.size());
+ //LOG(ERROR) << addrs;
+ }
+}
+
+#if !TD_EVENTFD_UNSUPPORTED
+TEST(Port, EventFdAndSignals) {
+ td::set_signal_handler(td::SignalType::User, [](int signal) {}).ensure();
+ SCOPE_EXIT {
+ td::set_signal_handler(td::SignalType::User, nullptr).ensure();
+ };
+
+ std::atomic_flag flag;
+ flag.test_and_set();
+ auto main_thread = pthread_self();
+ td::thread interrupt_thread{[&flag, &main_thread] {
+ td::setup_signals_alt_stack().ensure();
+ while (flag.test_and_set()) {
+ pthread_kill(main_thread, SIGUSR1);
+ td::usleep_for(1000 * td::Random::fast(1, 10)); // 0.001s - 0.01s
+ }
+ }};
+
+ for (int timeout_ms : {0, 1, 2, 10, 100, 500}) {
+ double min_diff = 10000000;
+ double max_diff = 0;
+ for (int t = 0; t < td::max(5, 1000 / td::max(timeout_ms, 1)); t++) {
+ td::EventFd event_fd;
+ event_fd.init();
+ auto start = td::Timestamp::now();
+ event_fd.wait(timeout_ms);
+ auto end = td::Timestamp::now();
+ auto passed = end.at() - start.at();
+ auto diff = passed * 1000 - timeout_ms;
+ min_diff = td::min(min_diff, diff);
+ max_diff = td::max(max_diff, diff);
+ }
+
+ LOG_CHECK(min_diff >= 0) << min_diff;
+ // LOG_CHECK(max_diff < 10) << max_diff;
+ LOG(INFO) << min_diff << " " << max_diff;
+ }
+ flag.clear();
+}
+#endif
+#endif
+
+#if TD_HAVE_THREAD_AFFINITY
+TEST(Port, ThreadAffinityMask) {
+ auto thread_id = td::this_thread::get_id();
+ auto old_mask = td::thread::get_affinity_mask(thread_id);
+ LOG(INFO) << "Initial thread " << thread_id << " affinity mask: " << old_mask;
+ for (size_t i = 0; i < 64; i++) {
+ auto mask = td::thread::get_affinity_mask(thread_id);
+ LOG(INFO) << mask;
+ auto result = td::thread::set_affinity_mask(thread_id, static_cast<td::uint64>(1) << i);
+ LOG(INFO) << i << ": " << result << ' ' << td::thread::get_affinity_mask(thread_id);
+
+ if (i <= 1) {
+ td::thread thread([] {
+ auto thread_id = td::this_thread::get_id();
+ auto mask = td::thread::get_affinity_mask(thread_id);
+ LOG(INFO) << "New thread " << thread_id << " affinity mask: " << mask;
+ auto result = td::thread::set_affinity_mask(thread_id, 1);
+ LOG(INFO) << "Thread " << thread_id << ": " << result << ' ' << td::thread::get_affinity_mask(thread_id);
+ });
+ LOG(INFO) << "Will join new thread " << thread.get_id()
+ << " with affinity mask: " << td::thread::get_affinity_mask(thread.get_id());
+ }
+ }
+ auto result = td::thread::set_affinity_mask(thread_id, old_mask);
+ LOG(INFO) << result;
+ old_mask = td::thread::get_affinity_mask(thread_id);
+ LOG(INFO) << old_mask;
+}
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/pq.cpp b/protocols/Telegram/tdlib/td/tdutils/test/pq.cpp
index 5210cc2638..d919d661b5 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/pq.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/pq.cpp
@@ -1,29 +1,24 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/utils/tests.h"
+#include "td/utils/algorithm.h"
#include "td/utils/BigNum.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
-#include "td/utils/misc.h"
+#include "td/utils/SliceBuilder.h"
-#include <algorithm>
#include <limits>
#include <utility>
-REGISTER_TESTS(pq)
-
-using namespace td;
-
-#if TD_HAVE_OPENSSL
-static bool is_prime(uint64 x) {
- for (uint64 d = 2; d < x && d * d <= x; d++) {
+static bool is_prime(td::uint64 x) {
+ for (td::uint64 d = 2; d < x && d * d <= x; d++) {
if (x % d == 0) {
return false;
}
@@ -31,9 +26,9 @@ static bool is_prime(uint64 x) {
return true;
}
-static std::vector<uint64> gen_primes(uint64 L, uint64 R, int limit = 0) {
- std::vector<uint64> res;
- for (auto x = L; x <= R && (limit <= 0 || res.size() < static_cast<std::size_t>(limit)); x++) {
+static td::vector<td::uint64> gen_primes(td::uint64 L, td::uint64 R, std::size_t limit = 0) {
+ td::vector<td::uint64> res;
+ for (auto x = L; x <= R && (limit <= 0 || res.size() < limit); x++) {
if (is_prime(x)) {
res.push_back(x);
}
@@ -41,22 +36,26 @@ static std::vector<uint64> gen_primes(uint64 L, uint64 R, int limit = 0) {
return res;
}
-static std::vector<uint64> gen_primes() {
- std::vector<uint64> result;
- append(result, gen_primes(1, 100));
- append(result, gen_primes((1ull << 31) - 500000, std::numeric_limits<uint64>::max(), 5));
- append(result, gen_primes((1ull << 32) - 500000, std::numeric_limits<uint64>::max(), 5));
- append(result, gen_primes((1ull << 39) - 500000, std::numeric_limits<uint64>::max(), 1));
+static td::vector<td::uint64> gen_primes(int mode) {
+ td::vector<td::uint64> result;
+ if (mode == 1) {
+ for (size_t i = 10; i <= 19; i++) {
+ td::append(result, gen_primes(i * 100000000, (i + 1) * 100000000, 1));
+ }
+ } else {
+ td::append(result, gen_primes(1, 100));
+ td::append(result, gen_primes((1ull << 31) - 500000, std::numeric_limits<td::uint64>::max(), 5));
+ td::append(result, gen_primes((1ull << 32) - 500000, std::numeric_limits<td::uint64>::max(), 2));
+ td::append(result, gen_primes((1ull << 39) - 500000, std::numeric_limits<td::uint64>::max(), 1));
+ }
return result;
}
-using PqQuery = std::pair<uint64, uint64>;
-static bool cmp(const PqQuery &a, const PqQuery &b) {
- return a.first * a.second < b.first * b.second;
-}
-static std::vector<PqQuery> gen_pq_queries() {
- std::vector<PqQuery> res;
- auto primes = gen_primes();
+using PqQuery = std::pair<td::uint64, td::uint64>;
+
+static td::vector<PqQuery> gen_pq_queries(int mode = 0) {
+ td::vector<PqQuery> res;
+ auto primes = gen_primes(mode);
for (auto q : primes) {
for (auto p : primes) {
if (p > q) {
@@ -65,28 +64,56 @@ static std::vector<PqQuery> gen_pq_queries() {
res.emplace_back(p, q);
}
}
- std::sort(res.begin(), res.end(), cmp);
return res;
}
-static void test_pq(uint64 first, uint64 second) {
- BigNum p = BigNum::from_decimal(PSLICE() << first);
- BigNum q = BigNum::from_decimal(PSLICE() << second);
+static td::string to_binary(td::uint64 x) {
+ td::string result;
+ do {
+ result = static_cast<char>(x & 255) + result;
+ x >>= 8;
+ } while (x > 0);
+ return result;
+}
+
+static void test_pq_fast(td::uint64 first, td::uint64 second) {
+ if ((static_cast<td::uint64>(1) << 63) / first <= second) {
+ return;
+ }
+
+ td::string p_str;
+ td::string q_str;
+ int err = td::pq_factorize(to_binary(first * second), &p_str, &q_str);
+ ASSERT_EQ(err, 0);
+
+ ASSERT_STREQ(p_str, to_binary(first));
+ ASSERT_STREQ(q_str, to_binary(second));
+}
+
+#if TD_HAVE_OPENSSL
+static void test_pq_slow(td::uint64 first, td::uint64 second) {
+ if ((static_cast<td::uint64>(1) << 63) / first > second) {
+ return;
+ }
+
+ td::BigNum p = td::BigNum::from_decimal(PSLICE() << first).move_as_ok();
+ td::BigNum q = td::BigNum::from_decimal(PSLICE() << second).move_as_ok();
- BigNum pq;
- BigNumContext context;
- BigNum::mul(pq, p, q, context);
- std::string pq_str = pq.to_binary();
+ td::BigNum pq;
+ td::BigNumContext context;
+ td::BigNum::mul(pq, p, q, context);
+ td::string pq_str = pq.to_binary();
- std::string p_str, q_str;
+ td::string p_str;
+ td::string q_str;
int err = td::pq_factorize(pq_str, &p_str, &q_str);
- CHECK(err == 0) << first << " * " << second;
+ LOG_CHECK(err == 0) << first << " * " << second;
- BigNum p_res = BigNum::from_binary(p_str);
- BigNum q_res = BigNum::from_binary(q_str);
+ td::BigNum p_res = td::BigNum::from_binary(p_str);
+ td::BigNum q_res = td::BigNum::from_binary(q_str);
- CHECK(p_str == p.to_binary()) << td::tag("got", p_res.to_decimal()) << td::tag("expected", first);
- CHECK(q_str == q.to_binary()) << td::tag("got", q_res.to_decimal()) << td::tag("expected", second);
+ LOG_CHECK(p_str == p.to_binary()) << td::tag("got", p_res.to_decimal()) << td::tag("expected", first);
+ LOG_CHECK(q_str == q.to_binary()) << td::tag("got", q_res.to_decimal()) << td::tag("expected", second);
}
#endif
@@ -101,18 +128,35 @@ TEST(CryptoPQ, hands) {
ASSERT_EQ(179424611ull, td::pq_factorize(179424611ull * 179424673ull));
#if TD_HAVE_OPENSSL
- test_pq(4294467311, 4294467449);
+ test_pq_slow(4294467311, 4294467449);
#endif
}
-#if TD_HAVE_OPENSSL
-TEST(CryptoPQ, generated_slow) {
+TEST(CryptoPQ, four) {
for (int i = 0; i < 100000; i++) {
- test_pq(2, 2);
+ test_pq_fast(2, 2);
+ }
+}
+
+TEST(CryptoPQ, generated_fast) {
+ auto queries = gen_pq_queries();
+ for (const auto &query : queries) {
+ test_pq_fast(query.first, query.second);
}
+}
+
+TEST(CryptoPQ, generated_server) {
+ auto queries = gen_pq_queries(1);
+ for (const auto &query : queries) {
+ test_pq_fast(query.first, query.second);
+ }
+}
+
+#if TD_HAVE_OPENSSL
+TEST(CryptoPQ, generated_slow) {
auto queries = gen_pq_queries();
- for (auto query : queries) {
- test_pq(query.first, query.second);
+ for (const auto &query : queries) {
+ test_pq_slow(query.first, query.second);
}
}
-#endif \ No newline at end of file
+#endif
diff --git a/protocols/Telegram/tdlib/td/tdutils/test/variant.cpp b/protocols/Telegram/tdlib/td/tdutils/test/variant.cpp
index 5c5e18d1d8..755acdfa98 100644
--- a/protocols/Telegram/tdlib/td/tdutils/test/variant.cpp
+++ b/protocols/Telegram/tdlib/td/tdutils/test/variant.cpp
@@ -1,5 +1,5 @@
//
-// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -9,22 +9,18 @@
#include "td/utils/tests.h"
#include "td/utils/Variant.h"
-REGISTER_TESTS(variant);
-
-using namespace td;
-
static const size_t BUF_SIZE = 1024 * 1024;
static char buf[BUF_SIZE], buf2[BUF_SIZE];
-static StringBuilder sb(MutableSlice(buf, BUF_SIZE - 1));
-static StringBuilder sb2(MutableSlice(buf2, BUF_SIZE - 1));
+static td::StringBuilder sb(td::MutableSlice(buf, BUF_SIZE - 1));
+static td::StringBuilder sb2(td::MutableSlice(buf2, BUF_SIZE - 1));
-static std::string move_sb() {
+static td::string move_sb() {
auto res = sb.as_cslice().str();
sb.clear();
return res;
}
-static std::string name(int id) {
+static td::string name(int id) {
if (id == 1) {
return "A";
}
@@ -58,18 +54,18 @@ using C = Class<3>;
TEST(Variant, simple) {
{
- Variant<std::unique_ptr<A>, std::unique_ptr<B>, std::unique_ptr<C>> abc;
+ td::Variant<td::unique_ptr<A>, td::unique_ptr<B>, td::unique_ptr<C>> abc;
ASSERT_STREQ("", sb.as_cslice());
- abc = std::make_unique<A>();
+ abc = td::make_unique<A>();
ASSERT_STREQ("+A", sb.as_cslice());
sb.clear();
- abc = std::make_unique<B>();
+ abc = td::make_unique<B>();
ASSERT_STREQ("+B-A", sb.as_cslice());
sb.clear();
- abc = std::make_unique<C>();
+ abc = td::make_unique<C>();
ASSERT_STREQ("+C-B", sb.as_cslice());
sb.clear();
}
ASSERT_STREQ("-C", move_sb());
sb.clear();
-};
+}