/root/bitcoin/src/test/util/net.cpp
Line | Count | Source |
1 | | // Copyright (c) 2020-present The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <test/util/net.h> |
6 | | |
7 | | #include <net.h> |
8 | | #include <net_processing.h> |
9 | | #include <netaddress.h> |
10 | | #include <netmessagemaker.h> |
11 | | #include <node/connection_types.h> |
12 | | #include <node/eviction.h> |
13 | | #include <protocol.h> |
14 | | #include <random.h> |
15 | | #include <serialize.h> |
16 | | #include <span.h> |
17 | | #include <sync.h> |
18 | | |
19 | | #include <chrono> |
20 | | #include <optional> |
21 | | #include <vector> |
22 | | |
23 | | void ConnmanTestMsg::Handshake(CNode& node, |
24 | | bool successfully_connected, |
25 | | ServiceFlags remote_services, |
26 | | ServiceFlags local_services, |
27 | | int32_t version, |
28 | | bool relay_txs) |
29 | 20.5k | { |
30 | 20.5k | auto& peerman{static_cast<PeerManager&>(*m_msgproc)}; |
31 | 20.5k | auto& connman{*this}; |
32 | | |
33 | 20.5k | peerman.InitializeNode(node, local_services); |
34 | 20.5k | peerman.SendMessages(node); |
35 | 20.5k | FlushSendBuffer(node); // Drop the version message added by SendMessages. |
36 | | |
37 | 20.5k | CSerializedNetMsg msg_version{ |
38 | 20.5k | NetMsg::Make(NetMsgType::VERSION, |
39 | 20.5k | version, // |
40 | 20.5k | Using<CustomUintFormatter<8>>(remote_services), // |
41 | 20.5k | int64_t{}, // dummy time |
42 | 20.5k | int64_t{}, // ignored service bits |
43 | 20.5k | CNetAddr::V1(CService{}), // dummy |
44 | 20.5k | int64_t{}, // ignored service bits |
45 | 20.5k | CNetAddr::V1(CService{}), // ignored |
46 | 20.5k | uint64_t{1}, // dummy nonce |
47 | 20.5k | std::string{}, // dummy subver |
48 | 20.5k | int32_t{}, // dummy starting_height |
49 | 20.5k | relay_txs), |
50 | 20.5k | }; |
51 | | |
52 | 20.5k | (void)connman.ReceiveMsgFrom(node, std::move(msg_version)); |
53 | 20.5k | node.fPauseSend = false; |
54 | 20.5k | connman.ProcessMessagesOnce(node); |
55 | 20.5k | peerman.SendMessages(node); |
56 | 20.5k | FlushSendBuffer(node); // Drop the verack message added by SendMessages. |
57 | 20.5k | if (node.fDisconnect) return; Branch (57:9): [True: 4.79k, False: 15.7k]
|
58 | 20.5k | assert(node.nVersion == version); Branch (58:5): [True: 15.7k, False: 0]
|
59 | 15.7k | assert(node.GetCommonVersion() == std::min(version, node.AdvertisedVersion())); Branch (59:5): [True: 15.7k, False: 0]
|
60 | 15.7k | CNodeStateStats statestats; |
61 | 15.7k | assert(peerman.GetNodeStateStats(node.GetId(), statestats)); Branch (61:5): [True: 15.7k, False: 0]
|
62 | 15.7k | assert(statestats.m_relay_txs == (relay_txs && !node.IsBlockOnlyConn())); Branch (62:5): [True: 12.6k, False: 3.12k]
Branch (62:5): [True: 11.8k, False: 786]
Branch (62:5): [True: 15.7k, False: 0]
|
63 | 15.7k | assert(statestats.their_services == remote_services); Branch (63:5): [True: 15.7k, False: 0]
|
64 | 15.7k | if (successfully_connected) { Branch (64:9): [True: 13.3k, False: 2.39k]
|
65 | 13.3k | CSerializedNetMsg msg_verack{NetMsg::Make(NetMsgType::VERACK)}; |
66 | 13.3k | (void)connman.ReceiveMsgFrom(node, std::move(msg_verack)); |
67 | 13.3k | node.fPauseSend = false; |
68 | 13.3k | connman.ProcessMessagesOnce(node); |
69 | 13.3k | peerman.SendMessages(node); |
70 | 13.3k | assert(node.fSuccessfullyConnected == true); Branch (70:9): [True: 13.3k, False: 0]
|
71 | 13.3k | } |
72 | 15.7k | } |
73 | | |
74 | 9.33k | void ConnmanTestMsg::ResetAddrCache() { m_addr_response_caches = {}; } |
75 | | |
76 | | void ConnmanTestMsg::ResetMaxOutboundCycle() |
77 | 9.33k | { |
78 | 9.33k | LOCK(m_total_bytes_sent_mutex); |
79 | 9.33k | nMaxOutboundCycleStartTime = 0s; |
80 | 9.33k | nMaxOutboundTotalBytesSentInCycle = 0; |
81 | 9.33k | } |
82 | | |
83 | | void ConnmanTestMsg::Reset() |
84 | 9.33k | { |
85 | 9.33k | ResetAddrCache(); |
86 | 9.33k | ResetMaxOutboundCycle(); |
87 | 9.33k | m_private_broadcast.m_outbound_tor_ok_at_least_once.store(false); |
88 | 9.33k | m_private_broadcast.m_num_to_open.store(0); |
89 | 9.33k | } |
90 | | |
91 | | void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, std::span<const uint8_t> msg_bytes, bool& complete) const |
92 | 949k | { |
93 | 949k | assert(node.ReceiveMsgBytes(msg_bytes, complete)); Branch (93:5): [True: 949k, False: 0]
|
94 | 949k | if (complete) { Branch (94:9): [True: 472k, False: 477k]
|
95 | 472k | node.MarkReceivedMsgsForProcessing(); |
96 | 472k | } |
97 | 949k | } |
98 | | |
99 | | void ConnmanTestMsg::FlushSendBuffer(CNode& node) const |
100 | 493k | { |
101 | 493k | LOCK(node.cs_vSend); |
102 | 493k | node.vSendMsg.clear(); |
103 | 493k | node.m_send_memusage = 0; |
104 | 542k | while (true) { Branch (104:12): [Folded - Ignored]
|
105 | 542k | const auto& [to_send, _more, _msg_type] = node.m_transport->GetBytesToSend(false); |
106 | 542k | if (to_send.empty()) break; Branch (106:13): [True: 493k, False: 49.2k]
|
107 | 49.2k | node.m_transport->MarkBytesSent(to_send.size()); |
108 | 49.2k | } |
109 | 493k | } |
110 | | |
111 | | bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) const |
112 | 486k | { |
113 | 486k | bool queued = node.m_transport->SetMessageToSend(ser_msg); |
114 | 486k | assert(queued); Branch (114:5): [True: 486k, False: 0]
|
115 | 486k | bool complete{false}; |
116 | 1.43M | while (true) { Branch (116:12): [Folded - Ignored]
|
117 | 1.43M | const auto& [to_send, _more, _msg_type] = node.m_transport->GetBytesToSend(false); |
118 | 1.43M | if (to_send.empty()) break; Branch (118:13): [True: 486k, False: 949k]
|
119 | 949k | NodeReceiveMsgBytes(node, to_send, complete); |
120 | 949k | node.m_transport->MarkBytesSent(to_send.size()); |
121 | 949k | } |
122 | 486k | return complete; |
123 | 486k | } |
124 | | |
125 | | CNode* ConnmanTestMsg::ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type) |
126 | 0 | { |
127 | 0 | CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true, /*proxy_override=*/std::nullopt); |
128 | 0 | if (!node) return nullptr; Branch (128:9): [True: 0, False: 0]
|
129 | 0 | node->SetCommonVersion(PROTOCOL_VERSION); |
130 | 0 | peerman.InitializeNode(*node, ServiceFlags(NODE_NETWORK | NODE_WITNESS)); |
131 | 0 | node->fSuccessfullyConnected = true; |
132 | 0 | AddTestNode(*node); |
133 | 0 | return node; |
134 | 0 | } |
135 | | |
136 | | std::vector<NodeEvictionCandidate> GetRandomNodeEvictionCandidates(int n_candidates, FastRandomContext& random_context) |
137 | 0 | { |
138 | 0 | std::vector<NodeEvictionCandidate> candidates; |
139 | 0 | candidates.reserve(n_candidates); |
140 | 0 | for (int id = 0; id < n_candidates; ++id) { Branch (140:22): [True: 0, False: 0]
|
141 | 0 | candidates.push_back({ |
142 | 0 | .id=id, |
143 | 0 | .m_connected=NodeSeconds{std::chrono::seconds{random_context.randrange(100)}}, |
144 | 0 | .m_min_ping_time=std::chrono::microseconds{random_context.randrange(100)}, |
145 | 0 | .m_last_block_time=std::chrono::seconds{random_context.randrange(100)}, |
146 | 0 | .m_last_tx_time=std::chrono::seconds{random_context.randrange(100)}, |
147 | 0 | .fRelevantServices=random_context.randbool(), |
148 | 0 | .m_relay_txs=random_context.randbool(), |
149 | 0 | .fBloomFilter=random_context.randbool(), |
150 | 0 | .nKeyedNetGroup=random_context.randrange(100u), |
151 | 0 | .prefer_evict=random_context.randbool(), |
152 | 0 | .m_is_local=random_context.randbool(), |
153 | 0 | .m_network=ALL_NETWORKS[random_context.randrange(ALL_NETWORKS.size())], |
154 | 0 | .m_noban=false, |
155 | 0 | .m_conn_type=ConnectionType::INBOUND, |
156 | 0 | }); |
157 | 0 | } |
158 | 0 | return candidates; |
159 | 0 | } |
160 | | |
161 | | // Have different ZeroSock (or others that inherit from it) objects have different |
162 | | // m_socket because EqualSharedPtrSock compares m_socket and we want to avoid two |
163 | | // different objects comparing as equal. |
164 | | static std::atomic<SOCKET> g_mocked_sock_fd{0}; |
165 | | |
166 | 0 | ZeroSock::ZeroSock() : Sock{g_mocked_sock_fd++} {} |
167 | | |
168 | | // Sock::~Sock() would try to close(2) m_socket if it is not INVALID_SOCKET, avoid that. |
169 | 0 | ZeroSock::~ZeroSock() { m_socket = INVALID_SOCKET; } |
170 | | |
171 | 0 | ssize_t ZeroSock::Send(const void*, size_t len, int) const { return len; } |
172 | | |
173 | | ssize_t ZeroSock::Recv(void* buf, size_t len, int flags) const |
174 | 0 | { |
175 | 0 | memset(buf, 0x0, len); |
176 | 0 | return len; |
177 | 0 | } |
178 | | |
179 | 0 | int ZeroSock::Connect(const sockaddr*, socklen_t) const { return 0; } |
180 | | |
181 | 0 | int ZeroSock::Bind(const sockaddr*, socklen_t) const { return 0; } |
182 | | |
183 | 0 | int ZeroSock::Listen(int) const { return 0; } |
184 | | |
185 | | std::unique_ptr<Sock> ZeroSock::Accept(sockaddr* addr, socklen_t* addr_len) const |
186 | 0 | { |
187 | 0 | if (addr != nullptr) { Branch (187:9): [True: 0, False: 0]
|
188 | | // Pretend all connections come from 5.5.5.5:6789 |
189 | 0 | memset(addr, 0x00, *addr_len); |
190 | 0 | const socklen_t write_len = static_cast<socklen_t>(sizeof(sockaddr_in)); |
191 | 0 | if (*addr_len >= write_len) { Branch (191:13): [True: 0, False: 0]
|
192 | 0 | *addr_len = write_len; |
193 | 0 | sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(addr); |
194 | 0 | addr_in->sin_family = AF_INET; |
195 | 0 | memset(&addr_in->sin_addr, 0x05, sizeof(addr_in->sin_addr)); |
196 | 0 | addr_in->sin_port = htons(6789); |
197 | 0 | } |
198 | 0 | } |
199 | 0 | return std::make_unique<ZeroSock>(); |
200 | 0 | } |
201 | | |
202 | | int ZeroSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const |
203 | 0 | { |
204 | 0 | std::memset(opt_val, 0x0, *opt_len); |
205 | 0 | return 0; |
206 | 0 | } |
207 | | |
208 | 0 | int ZeroSock::SetSockOpt(int, int, const void*, socklen_t) const { return 0; } |
209 | | |
210 | | int ZeroSock::GetSockName(sockaddr* name, socklen_t* name_len) const |
211 | 0 | { |
212 | 0 | std::memset(name, 0x0, *name_len); |
213 | 0 | return 0; |
214 | 0 | } |
215 | | |
216 | 0 | bool ZeroSock::SetNonBlocking() const { return true; } |
217 | | |
218 | 0 | bool ZeroSock::IsSelectable() const { return true; } |
219 | | |
220 | | bool ZeroSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const |
221 | 0 | { |
222 | 0 | if (occurred != nullptr) { Branch (222:9): [True: 0, False: 0]
|
223 | 0 | *occurred = requested; |
224 | 0 | } |
225 | 0 | return true; |
226 | 0 | } |
227 | | |
228 | | bool ZeroSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const |
229 | 0 | { |
230 | 0 | for (auto& [sock, events] : events_per_sock) { Branch (230:31): [True: 0, False: 0]
|
231 | 0 | (void)sock; |
232 | 0 | events.occurred = events.requested; |
233 | 0 | } |
234 | 0 | return true; |
235 | 0 | } |
236 | | |
237 | | ZeroSock& ZeroSock::operator=(Sock&& other) |
238 | 0 | { |
239 | 0 | assert(false && "Move of Sock into ZeroSock not allowed."); Branch (239:5): [Folded - Ignored]
Branch (239:5): [Folded - Ignored]
Branch (239:5): [Folded - Ignored]
|
240 | 0 | return *this; |
241 | 0 | } |
242 | | |
243 | | StaticContentsSock::StaticContentsSock(const std::string& contents) |
244 | 0 | : m_contents{contents} |
245 | 0 | { |
246 | 0 | } |
247 | | |
248 | | ssize_t StaticContentsSock::Recv(void* buf, size_t len, int flags) const |
249 | 0 | { |
250 | 0 | const size_t consume_bytes{std::min(len, m_contents.size() - m_consumed)}; |
251 | 0 | std::memcpy(buf, m_contents.data() + m_consumed, consume_bytes); |
252 | 0 | if ((flags & MSG_PEEK) == 0) { Branch (252:9): [True: 0, False: 0]
|
253 | 0 | m_consumed += consume_bytes; |
254 | 0 | } |
255 | 0 | return consume_bytes; |
256 | 0 | } |
257 | | |
258 | | StaticContentsSock& StaticContentsSock::operator=(Sock&& other) |
259 | 0 | { |
260 | 0 | assert(false && "Move of Sock into StaticContentsSock not allowed."); Branch (260:5): [Folded - Ignored]
Branch (260:5): [Folded - Ignored]
Branch (260:5): [Folded - Ignored]
|
261 | 0 | return *this; |
262 | 0 | } |
263 | | |
264 | | ssize_t DynSock::Pipe::GetBytes(void* buf, size_t len, int flags) |
265 | 0 | { |
266 | 0 | WAIT_LOCK(m_mutex, lock); |
267 | |
|
268 | 0 | if (m_data.empty()) { Branch (268:9): [True: 0, False: 0]
|
269 | 0 | if (m_eof) { Branch (269:13): [True: 0, False: 0]
|
270 | 0 | return 0; |
271 | 0 | } |
272 | 0 | errno = EAGAIN; // Same as recv(2) on a non-blocking socket. |
273 | 0 | return -1; |
274 | 0 | } |
275 | | |
276 | 0 | const size_t read_bytes{std::min(len, m_data.size())}; |
277 | |
|
278 | 0 | std::memcpy(buf, m_data.data(), read_bytes); |
279 | 0 | if ((flags & MSG_PEEK) == 0) { Branch (279:9): [True: 0, False: 0]
|
280 | 0 | m_data.erase(m_data.begin(), m_data.begin() + read_bytes); |
281 | 0 | } |
282 | |
|
283 | 0 | return read_bytes; |
284 | 0 | } |
285 | | |
286 | | std::optional<CNetMessage> DynSock::Pipe::GetNetMsg() |
287 | 0 | { |
288 | 0 | V1Transport transport{NodeId{0}}; |
289 | |
|
290 | 0 | { |
291 | 0 | WAIT_LOCK(m_mutex, lock); |
292 | |
|
293 | 0 | WaitForDataOrEof(lock); |
294 | 0 | if (m_eof && m_data.empty()) { Branch (294:13): [True: 0, False: 0]
Branch (294:22): [True: 0, False: 0]
|
295 | 0 | return std::nullopt; |
296 | 0 | } |
297 | | |
298 | 0 | for (;;) { |
299 | 0 | std::span<const uint8_t> s{m_data}; |
300 | 0 | if (!transport.ReceivedBytes(s)) { // Consumed bytes are removed from the front of s. Branch (300:17): [True: 0, False: 0]
|
301 | 0 | return std::nullopt; |
302 | 0 | } |
303 | 0 | m_data.erase(m_data.begin(), m_data.begin() + m_data.size() - s.size()); |
304 | 0 | if (transport.ReceivedMessageComplete()) { Branch (304:17): [True: 0, False: 0]
|
305 | 0 | break; |
306 | 0 | } |
307 | 0 | if (m_data.empty()) { Branch (307:17): [True: 0, False: 0]
|
308 | 0 | WaitForDataOrEof(lock); |
309 | 0 | if (m_eof && m_data.empty()) { Branch (309:21): [True: 0, False: 0]
Branch (309:30): [True: 0, False: 0]
|
310 | 0 | return std::nullopt; |
311 | 0 | } |
312 | 0 | } |
313 | 0 | } |
314 | 0 | } |
315 | | |
316 | 0 | bool reject{false}; |
317 | 0 | CNetMessage msg{transport.GetReceivedMessage(/*time=*/{}, reject)}; |
318 | 0 | if (reject) { Branch (318:9): [True: 0, False: 0]
|
319 | 0 | return std::nullopt; |
320 | 0 | } |
321 | 0 | return std::make_optional<CNetMessage>(std::move(msg)); |
322 | 0 | } |
323 | | |
324 | | void DynSock::Pipe::PushBytes(const void* buf, size_t len) |
325 | 0 | { |
326 | 0 | LOCK(m_mutex); |
327 | 0 | const uint8_t* b = static_cast<const uint8_t*>(buf); |
328 | 0 | m_data.insert(m_data.end(), b, b + len); |
329 | 0 | m_cond.notify_all(); |
330 | 0 | } |
331 | | |
332 | | void DynSock::Pipe::Eof() |
333 | 0 | { |
334 | 0 | LOCK(m_mutex); |
335 | 0 | m_eof = true; |
336 | 0 | m_cond.notify_all(); |
337 | 0 | } |
338 | | |
339 | | void DynSock::Pipe::WaitForDataOrEof(UniqueLock<Mutex>& lock) |
340 | 0 | { |
341 | 0 | Assert(lock.mutex() == &m_mutex); |
342 | |
|
343 | 0 | m_cond.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(m_mutex) { |
344 | 0 | AssertLockHeld(m_mutex); |
345 | 0 | return !m_data.empty() || m_eof; Branch (345:16): [True: 0, False: 0]
Branch (345:35): [True: 0, False: 0]
|
346 | 0 | }); |
347 | 0 | } |
348 | | |
349 | | DynSock::DynSock(std::shared_ptr<Pipes> pipes, Queue* accept_sockets) |
350 | 0 | : m_pipes{pipes}, m_accept_sockets{accept_sockets} |
351 | 0 | { |
352 | 0 | } |
353 | | |
354 | | DynSock::DynSock(std::shared_ptr<Pipes> pipes) |
355 | 0 | : m_pipes{pipes}, m_accept_sockets{} |
356 | 0 | { |
357 | 0 | } |
358 | | |
359 | | DynSock::~DynSock() |
360 | 0 | { |
361 | 0 | m_pipes->send.Eof(); |
362 | 0 | } |
363 | | |
364 | | ssize_t DynSock::Recv(void* buf, size_t len, int flags) const |
365 | 0 | { |
366 | 0 | return m_pipes->recv.GetBytes(buf, len, flags); |
367 | 0 | } |
368 | | |
369 | | ssize_t DynSock::Send(const void* buf, size_t len, int) const |
370 | 0 | { |
371 | 0 | m_pipes->send.PushBytes(buf, len); |
372 | 0 | return len; |
373 | 0 | } |
374 | | |
375 | | std::unique_ptr<Sock> DynSock::Accept(sockaddr* addr, socklen_t* addr_len) const |
376 | 0 | { |
377 | 0 | assert(m_accept_sockets && "Accept() called on non-listening DynSock"); Branch (377:5): [True: 0, False: 0]
Branch (377:5): [Folded - Ignored]
Branch (377:5): [True: 0, False: 0]
|
378 | 0 | ZeroSock::Accept(addr, addr_len); |
379 | 0 | return m_accept_sockets->Pop().value_or(nullptr); |
380 | 0 | } |
381 | | |
382 | | bool DynSock::Wait(std::chrono::milliseconds timeout, |
383 | | Event requested, |
384 | | Event* occurred) const |
385 | 0 | { |
386 | 0 | EventsPerSock ev; |
387 | 0 | ev.emplace(this, Events{requested}); |
388 | 0 | const bool ret{WaitMany(timeout, ev)}; |
389 | 0 | if (occurred != nullptr) { Branch (389:9): [True: 0, False: 0]
|
390 | 0 | *occurred = ev.begin()->second.occurred; |
391 | 0 | } |
392 | 0 | return ret; |
393 | 0 | } |
394 | | |
395 | | bool DynSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const |
396 | 0 | { |
397 | 0 | const auto deadline = std::chrono::steady_clock::now() + timeout; |
398 | 0 | bool at_least_one_event_occurred{false}; |
399 | |
|
400 | 0 | for (;;) { |
401 | | // Check all sockets for readiness without waiting. |
402 | 0 | for (auto& [sock, events] : events_per_sock) { Branch (402:35): [True: 0, False: 0]
|
403 | 0 | if ((events.requested & Sock::SendEvent) != 0) { Branch (403:17): [True: 0, False: 0]
|
404 | | // Always ready for Send(). |
405 | 0 | events.occurred |= Sock::SendEvent; |
406 | 0 | at_least_one_event_occurred = true; |
407 | 0 | } |
408 | |
|
409 | 0 | if ((events.requested & Sock::RecvEvent) != 0) { Branch (409:17): [True: 0, False: 0]
|
410 | 0 | auto dyn_sock = reinterpret_cast<const DynSock*>(sock.get()); |
411 | 0 | uint8_t b; |
412 | 0 | if (dyn_sock->m_pipes->recv.GetBytes(&b, 1, MSG_PEEK) == 1 || (dyn_sock->m_accept_sockets && !dyn_sock->m_accept_sockets->Empty())) { Branch (412:21): [True: 0, False: 0]
Branch (412:80): [True: 0, False: 0]
Branch (412:110): [True: 0, False: 0]
|
413 | 0 | events.occurred |= Sock::RecvEvent; |
414 | 0 | at_least_one_event_occurred = true; |
415 | 0 | } |
416 | 0 | } |
417 | 0 | } |
418 | |
|
419 | 0 | if (at_least_one_event_occurred || std::chrono::steady_clock::now() > deadline) { Branch (419:13): [True: 0, False: 0]
Branch (419:13): [True: 0, False: 0]
Branch (419:44): [True: 0, False: 0]
|
420 | 0 | break; |
421 | 0 | } |
422 | | |
423 | 0 | std::this_thread::sleep_for(10ms); |
424 | 0 | } |
425 | |
|
426 | 0 | return true; |
427 | 0 | } |
428 | | |
429 | | DynSock& DynSock::operator=(Sock&&) |
430 | 0 | { |
431 | 0 | assert(false && "Move of Sock into DynSock not allowed."); Branch (431:5): [Folded - Ignored]
Branch (431:5): [Folded - Ignored]
Branch (431:5): [Folded - Ignored]
|
432 | 0 | return *this; |
433 | 0 | } |