1 //====== Copyright Valve Corporation, All rights reserved. ====================
2 //
3 // Common stuff used by SteamNetworkingSockets code
4 //
5 //=============================================================================
6
7 #ifndef STEAMNETWORKINGSOCKETS_INTERNAL_H
8 #define STEAMNETWORKINGSOCKETS_INTERNAL_H
9 #pragma once
10
11 // Public shared stuff
12 #include <tier0/basetypes.h>
13 #include <tier0/t0constants.h>
14 #include <tier0/platform.h>
15 #include <tier0/dbg.h>
16 #ifdef STEAMNETWORKINGSOCKETS_STEAMCLIENT
17 #include <tier0/validator.h>
18 #endif
19 #include <steam/steamnetworkingtypes.h>
20 #include <tier1/netadr.h>
21 #include <vstdlib/strtools.h>
22 #include <vstdlib/random.h>
23 #include <tier1/utlvector.h>
24 #include <tier1/utlbuffer.h>
25 #include "keypair.h"
26 #include <tier0/memdbgoff.h>
27 #include <steamnetworkingsockets_messages_certs.pb.h>
28 #include <steam/isteamnetworkingutils.h> // for the rendering helpers
29
30 #include <tier0/memdbgon.h>
31
32 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_MEM_OVERRIDE
33 #define STEAMNETWORKINGSOCKETS_DECLARE_CLASS_OPERATOR_NEW \
34 static void* operator new( size_t s ) noexcept { return malloc( s ); } \
35 static void* operator new[]( size_t ) = delete; \
36 static void operator delete( void *p ) noexcept { free( p ); } \
37 static void operator delete[]( void * ) = delete;
38 #else
39 #define STEAMNETWORKINGSOCKETS_DECLARE_CLASS_OPERATOR_NEW
40 #endif
41
42 // Enable SDR, except in opensource build
43 #ifndef STEAMNETWORKINGSOCKETS_OPENSOURCE
44 #define STEAMNETWORKINGSOCKETS_ENABLE_SDR
45 #endif
46
47 // Always enable ISteamNetworkingMessages, unless it is specifically
48 // disabled
49 #ifndef STEAMNETWORKINGSOCKETS_DISABLE_STEAMNETWORKINGMESSAGES
50 #define STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
51 #endif
52
53 #if !defined( STEAMNETWORKINGSOCKETS_OPENSOURCE ) && !defined( STEAMNETWORKINGSOCKETS_STREAMINGCLIENT )
54 // STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT means we know how to make a cert request from some sort of certificate authority
55 #define STEAMNETWORKINGSOCKETS_CAN_REQUEST_CERT
56 #endif
57
58 // Always #define STEAMNETWORKINGSOCKETS_ENABLE_ICE in a few places.
59 // You can also define it on the command line
60 #ifndef STEAMNETWORKINGSOCKETS_ENABLE_ICE
61 #if defined( STEAMNETWORKINGSOCKETS_STEAMCLIENT ) || defined( STEAMNETWORKINGSOCKETS_STREAMINGCLIENT )
62 #define STEAMNETWORKINGSOCKETS_ENABLE_ICE
63 #endif
64 #endif
65
66 // Enable diagnostics UI if there is a Steam client to display them
67 #ifndef STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI
68 #ifdef STEAMNETWORKINGSOCKETS_STEAM
69 #define STEAMNETWORKINGSOCKETS_ENABLE_DIAGNOSTICSUI
70 #endif
71 #endif
72
73 /// Enumerate different kinds of transport that can be used
74 enum ESteamNetTransportKind
75 {
76 k_ESteamNetTransport_Unknown = 0,
77 k_ESteamNetTransport_LoopbackBuffers = 1, // Internal buffers, not using OS network stack
78 k_ESteamNetTransport_LocalHost = 2, // Using OS network stack to talk to localhost address
79 k_ESteamNetTransport_UDP = 3, // Ordinary UDP connection.
80 k_ESteamNetTransport_UDPProbablyLocal = 4, // Ordinary UDP connection over a route that appears to be "local", meaning we think it is probably fast. This is just a guess: VPNs and IPv6 make this pretty fuzzy.
81 k_ESteamNetTransport_TURN = 5, // Relayed over TURN server
82 k_ESteamNetTransport_SDRP2P = 6, // P2P connection relayed over Steam Datagram Relay
83 k_ESteamNetTransport_SDRHostedServer = 7, // Connection to a server hosted in a known data center via Steam Datagram Relay
84
85 k_ESteamNetTransport_Force32Bit = 0x7fffffff
86 };
87
88 // Redefine the macros for byte-swapping, to sure the correct
89 // argument size. We probably should move this into platform.h,
90 // but I suspect we'd find a bunch of "bugs" which currently don't
91 // matter because we don't support any big endian platforms right now
92 #ifdef PLAT_LITTLE_ENDIAN
93 #undef LittleWord
94 template <typename T>
LittleWord(T x)95 inline T LittleWord( T x )
96 {
97 COMPILE_TIME_ASSERT(sizeof(T) == 2);
98 return x;
99 }
100
101 #undef LittleDWord
102 template <typename T>
LittleDWord(T x)103 inline T LittleDWord( T x )
104 {
105 COMPILE_TIME_ASSERT(sizeof(T) == 4);
106 return x;
107 }
108
109 #undef LittleQWord
110 template <typename T>
LittleQWord(T x)111 inline T LittleQWord( T x )
112 {
113 COMPILE_TIME_ASSERT(sizeof(T) == 8);
114 return x;
115 }
116 #endif
117
118 #ifdef _WIN32
119 // Define iovec with the same field names as the POSIX one, but with the same memory layout
120 // as Winsock WSABUF thingy
121 struct iovec
122 {
123 unsigned long iov_len;
124 void *iov_base;
125 };
126 #else
127 #include <sys/uio.h>
128 #endif
129
130 // likely() and unlikely(). Branch hints
131 // This is an idiom from the linux kernel
132 #ifdef __GNUC__
133 #ifndef likely
134 #define likely(x) __builtin_expect (!!(x), 1)
135 #endif
136 #ifndef unlikely
137 #define unlikely(x) __builtin_expect (!!(x), 0)
138 #endif
139 #else
140 #ifndef likely
141 #define likely(x) (x)
142 #endif
143 #ifndef unlikely
144 #define unlikely(x) (x)
145 #endif
146 #endif
147
148 // Internal stuff goes in a private namespace
149 namespace SteamNetworkingSocketsLib {
150
151 // Determine serialized size of protobuf msg.
152 // Always return int, because size_t is dumb,
153 // and unsigned types are from the devil.
154 template <typename TMsg>
ProtoMsgByteSize(const TMsg & msg)155 inline int ProtoMsgByteSize( const TMsg &msg )
156 {
157 #if GOOGLE_PROTOBUF_VERSION < 3004000
158 return msg.ByteSize();
159 #else
160 return static_cast<int>( msg.ByteSizeLong() );
161 #endif
162 }
163
164 struct SteamDatagramLinkStats;
165 struct SteamDatagramLinkLifetimeStats;
166 struct SteamDatagramLinkInstantaneousStats;
167 struct SteamNetworkingDetailedConnectionStatus;
168
169 // An identity operator that always returns its operand.
170 // NOTE: std::hash is an identity operator on many compilers
171 // for the basic primitives. If you really need actual
172 // hashing, don't use std::hash!
173 template <typename T >
174 struct Identity
175 {
operatorIdentity176 const T &operator()( const T &x ) const { return x; }
177 };
178
179 /// Max size of UDP payload. Includes API payload and
180 /// any headers, but does not include IP/UDP headers
181 /// (IP addresses, ports, checksum, etc.
182 const int k_cbSteamNetworkingSocketsMaxUDPMsgLen = 1300;
183
184 /// Do not allow MTU to be set less than this
185 const int k_cbSteamNetworkingSocketsMinMTUPacketSize = 200;
186
187 /// Overhead that we will reserve for stats, etc when calculating the max
188 /// message that we won't fragment
189 const int k_cbSteamNetworkingSocketsNoFragmentHeaderReserve = 100;
190
191 /// Size of security tag for AES-GCM.
192 /// It would be nice to use a smaller tag, but BCrypt requires a 16-byte tag,
193 /// which is what OpenSSL uses by default for TLS.
194 const int k_cbSteamNetwokingSocketsEncrytionTagSize = 16;
195
196 /// Max length of plaintext and encrypted payload we will send. AES-GCM does
197 /// not use padding (but it does have the security tag). So this can be
198 /// arbitrary, it does not need to account for the block size.
199 const int k_cbSteamNetworkingSocketsMaxEncryptedPayloadSend = 1248;
200 const int k_cbSteamNetworkingSocketsMaxPlaintextPayloadSend = k_cbSteamNetworkingSocketsMaxEncryptedPayloadSend-k_cbSteamNetwokingSocketsEncrytionTagSize;
201
202 /// Use larger limits for what we are willing to receive.
203 const int k_cbSteamNetworkingSocketsMaxEncryptedPayloadRecv = k_cbSteamNetworkingSocketsMaxUDPMsgLen;
204 const int k_cbSteamNetworkingSocketsMaxPlaintextPayloadRecv = k_cbSteamNetworkingSocketsMaxUDPMsgLen;
205
206 /// If we have a cert that is going to expire in <N secondws, try to renew it
207 const int k_nSecCertExpirySeekRenew = 3600*2;
208
209 /// Make sure we have enough room for our headers and occasional inline pings and stats and such
210 /// FIXME - For relayed connections, we send some of the stats outside the encrypted block, so that
211 /// they can be observed by the relay. For direct connections, we put it in the encrypted block.
212 /// So we might need to adjust this to be per connection type instead off constant.
213 COMPILE_TIME_ASSERT( k_cbSteamNetworkingSocketsMaxEncryptedPayloadSend + 50 < k_cbSteamNetworkingSocketsMaxUDPMsgLen );
214
215 /// Min size of raw UDP message.
216 const int k_nMinSteamDatagramUDPMsgLen = 5;
217
218 /// When sending a stats message, what sort of reply is requested by the calling code?
219 enum EStatsReplyRequest
220 {
221 k_EStatsReplyRequest_NothingToSend, // We don't have anything to send at all
222 k_EStatsReplyRequest_NoReply, // We have something to send, but it does not require a reply
223 k_EStatsReplyRequest_DelayedOK, // We have something to send, but a delayed reply is OK
224 k_EStatsReplyRequest_Immediate, // Immediate reply is requested
225 };
226
227 /// Max time that we we should "Nagle" an ack, hoping to combine them together or
228 /// piggy back on another outgoing message, before sending a standalone message.
229 const SteamNetworkingMicroseconds k_usecMaxAckStatsDelay = 250*1000;
230
231 /// Max duration that a receiver could pend a data ack, in the hopes of trying
232 /// to piggyback the ack on another outbound packet.
233 /// !KLUDGE! This really ought to be application- (or connection-) specific.
234 const SteamNetworkingMicroseconds k_usecMaxDataAckDelay = 50*1000;
235
236 /// Precision of the delay ack delay values we send. A packed value of 1 represents 2^N microseconds
237 const unsigned k_usecAckDelayPacketSerializedPrecisionShift = 6;
238 COMPILE_TIME_ASSERT( ( (k_usecMaxAckStatsDelay*2) >> k_usecAckDelayPacketSerializedPrecisionShift ) < 0x4000 ); // Make sure we varint encode in 2 bytes, even if we overshoot a factor of 2x
239
240 /// After a connection is closed, a session will hang out in a CLOSE_WAIT-like
241 /// (or perhaps FIN_WAIT?) state to handle last stray packets and help both sides
242 /// close cleanly.
243 const SteamNetworkingMicroseconds k_usecSteamDatagramRouterCloseWait = k_nMillion*15;
244
245 // Internal reason codes
246 const int k_ESteamNetConnectionEnd_InternalRelay_SessionIdleTimeout = 9001;
247 const int k_ESteamNetConnectionEnd_InternalRelay_ClientChangedTarget = 9002;
248
249 /// Timeout value for pings. This basically determines the retry rate for pings.
250 /// If a ping is longer than this, then really, the server should not probably not be
251 /// considered available.
252 const SteamNetworkingMicroseconds k_usecSteamDatagramClientPingTimeout = 750000;
253
254 /// Keepalive interval for currently selected router. We send keepalive pings when
255 /// we haven't heard anything from the router in a while, to see if we need
256 /// to re-route.
257 const SteamNetworkingMicroseconds k_usecSteamDatagramClientPrimaryRouterKeepaliveInterval = 1 * k_nMillion;
258
259 /// Keepalive interval for backup routers. We send keepalive pings to
260 /// make sure our backup session still exists and we could switch to it
261 /// if it became necessary
262 const SteamNetworkingMicroseconds k_usecSteamDatagramClientBackupRouterKeepaliveInterval = 45 * k_nMillion;
263
264 /// Keepalive interval for gameserver. We send keepalive pings when we haven't
265 /// heard anything from the gameserver in a while, in order to try and deduce
266 /// where the router or gameserver are available.
267 const SteamNetworkingMicroseconds k_usecSteamDatagramClientServerKeepaliveInterval = 1 * k_nMillion;
268
269 /// Timeout value for session request messages
270 const SteamNetworkingMicroseconds k_usecSteamDatagramClientSessionRequestTimeout = 750000;
271
272 /// Router will continue to pend a client ping request for N microseconds,
273 /// hoping for an opportunity to send it inline.
274 const SteamNetworkingMicroseconds k_usecSteamDatagramRouterPendClientPing = 200000;
275
276 /// When serializing a "time since I last sent a packet" value into the packet,
277 /// what precision is used? (A serialized value of 1 = 2^N microseconds.)
278 const unsigned k_usecTimeSinceLastPacketSerializedPrecisionShift = 4;
279
280 /// "Time since last packet sent" values should be less than this.
281 /// Any larger value will be discarded, and should not be sent
282 const SteamNetworkingMicroseconds k_usecTimeSinceLastPacketMaxReasonable = k_nMillion/4;
283 COMPILE_TIME_ASSERT( ( k_usecTimeSinceLastPacketMaxReasonable >> k_usecTimeSinceLastPacketSerializedPrecisionShift ) < 0x8000 ); // make sure all "reasonable" values can get serialized into 16-bits
284
285 /// Don't send spacing values when packets are sent extremely close together. The spacing
286 /// should be a bit higher that our serialization precision.
287 const SteamNetworkingMicroseconds k_usecTimeSinceLastPacketMinReasonable = 2 << k_usecTimeSinceLastPacketSerializedPrecisionShift;
288
289 /// A really terrible ping score, but one that we can do some math with without overflowing
290 constexpr int k_nRouteScoreHuge = INT_MAX/8;
291
292 /// Protocol version of this code. This is a blunt instrument, which is incremented when we
293 /// wish to change the wire protocol in a way that doesn't have some other easy
294 /// mechanism for dealing with compatibility (e.g. using protobuf's robust mechanisms).
295 const uint32 k_nCurrentProtocolVersion = 10;
296
297 /// Minimum required version we will accept from a peer. We increment this
298 /// when we introduce wire breaking protocol changes and do not wish to be
299 /// backward compatible. This has been fine before the first major release,
300 /// but once we make a big public release, we probably won't ever be able to
301 /// do this again, and we'll need to have more sophisticated mechanisms.
302 const uint32 k_nMinRequiredProtocolVersion = 8;
303
304 /// SteamNetworkingMessages is built on top of SteamNetworkingSockets. We use a reserved
305 /// virtual port for this interface
306 const int k_nVirtualPort_Messages = 0x7fffffff;
307
308 // Serialize an UNSIGNED quantity. Returns pointer to the next byte.
309 // https://developers.google.com/protocol-buffers/docs/encoding
310 template <typename T>
SerializeVarInt(byte * p,T x)311 inline byte *SerializeVarInt( byte *p, T x )
312 {
313 while ( x >= (unsigned)0x80 ) // if you get a warning, it's because you are using a signed type! Don't use this for signed data!
314 {
315 // Truncate to 7 bits, and turn on the high bit, and write it.
316 *(p++) = byte( x | 0x80 );
317
318 // Move on to the next higher order bits.
319 x >>= 7U;
320 }
321 *p = x;
322 return p+1;
323 }
324
325 /// Serialize a bar int, but return null if we want to go past the end
326 template <typename T>
SerializeVarInt(byte * p,T x,const byte * pEnd)327 inline byte *SerializeVarInt( byte *p, T x, const byte *pEnd )
328 {
329 while ( x >= (unsigned)0x80 ) // if you get a warning, it's because you are using a signed type! Don't use this for signed data!
330 {
331 if ( p >= pEnd )
332 return nullptr;
333
334 // Truncate to 7 bits, and turn on the high bit, and write it.
335 *(p++) = byte( x | 0x80 );
336
337 // Move on to the next higher order bits.
338 x >>= 7U;
339 }
340 if ( p >= pEnd )
341 return nullptr;
342 *p = x;
343 return p+1;
344 }
345
VarIntSerializedSize(uint32 x)346 inline int VarIntSerializedSize( uint32 x )
347 {
348 if ( x < (1U<<7) ) return 1;
349 if ( x < (1U<<14) ) return 2;
350 if ( x < (1U<<21) ) return 3;
351 if ( x < (1U<<28) ) return 4;
352 return 5;
353 }
354
VarIntSerializedSize(uint64 x)355 inline int VarIntSerializedSize( uint64 x )
356 {
357 if ( x < (1LLU<<35) )
358 {
359 if ( x < (1LLU<<7) ) return 1;
360 if ( x < (1LLU<<14) ) return 2;
361 if ( x < (1LLU<<21) ) return 3;
362 if ( x < (1LLU<<28) ) return 4;
363 return 5;
364 }
365 if ( x < (1LLU<<42) ) return 6;
366 if ( x < (1LLU<<49) ) return 7;
367 if ( x < (1LLU<<56) ) return 8;
368 if ( x < (1LLU<<63) ) return 9;
369 return 10;
370 }
371
372 // De-serialize a var-int encoded quantity. Returns pointer to the next byte,
373 // or NULL if there was a decoding error (we hit the end of stream.)
374 // https://developers.google.com/protocol-buffers/docs/encoding
375 //
376 // NOTE: We do not detect overflow.
377 template <typename T>
DeserializeVarInt(byte * p,const byte * end,T & x)378 inline byte *DeserializeVarInt( byte *p, const byte *end, T &x )
379 {
380 if ( p >= end )
381 return nullptr;
382 T nResult = *p & 0x7f; // use local variable for working, to make sure compiler doesn't try to worry about pointer aliasing
383 unsigned nShift = 7;
384 while ( *(p++) & 0x80 )
385 {
386 if ( p >= end )
387 return nullptr;
388 nResult |= ( T( *p & 0x7f ) << nShift );
389 nShift += 7;
390 }
391 x = nResult;
392 return p;
393 }
394
395 // Const version
396 template <typename T>
DeserializeVarInt(const byte * p,const byte * end,T & x)397 inline const byte *DeserializeVarInt( const byte *p, const byte *end, T &x )
398 {
399 return DeserializeVarInt( const_cast<byte*>( p ), end, x );
400 }
401
402 void LinkStatsPrintInstantaneousToBuf( const char *pszLeader, const SteamDatagramLinkInstantaneousStats &stats, CUtlBuffer &buf );
403 void LinkStatsPrintLifetimeToBuf( const char *pszLeader, const SteamDatagramLinkLifetimeStats &stats, CUtlBuffer &buf );
404 void LinkStatsPrintToBuf( const char *pszLeader, const SteamDatagramLinkStats &stats, CUtlBuffer &buf );
405
406 class NumberPrettyPrinter
407 {
408 public:
NumberPrettyPrinter(int64 val)409 NumberPrettyPrinter( int64 val ) { Print(val); }
Print(int64 val)410 void Print( int64 val )
411 {
412 char *d = m_buf;
413 if ( val < 0 )
414 {
415 *(d++) = '-';
416 val = -val;
417 }
418 // Largest 64-bit (0x7fffffffffffffff) = 9,223,372,036,854,775,807
419 // which is 19 digits.
420 COMPILE_TIME_ASSERT( INT64_MAX > (int64)1e18 );
421 COMPILE_TIME_ASSERT( INT64_MAX/10 < (int64)1e18 );
422 int arnGroupsOfThree[6];
423 int nGroupsOfThree = 0;
424 while ( val >= 1000 )
425 {
426 arnGroupsOfThree[nGroupsOfThree++] = val % 1000;
427 val /= 1000;
428 }
429 int iVal = int( val ); // make sure compiler knows it can do 32-bit math
430 if ( iVal >= 100 ) { *(d++) = char( iVal/100 + '0' ); iVal %= 100; }
431 if ( iVal >= 10 ) { *(d++) = char( iVal/10 + '0' ); iVal %= 10; }
432 *(d++) = char( iVal + '0' );
433 while ( nGroupsOfThree > 0 )
434 {
435 int iThreeDigits = arnGroupsOfThree[--nGroupsOfThree];
436 *(d++) = ',';
437 *(d++) = char( iThreeDigits/100 ) + '0'; iThreeDigits %= 100;
438 *(d++) = char( iThreeDigits/10 ) + '0'; iThreeDigits %= 10;
439 *(d++) = char( iThreeDigits ) + '0';
440 }
441
442 *d = '\0';
443 }
String()444 inline const char *String() const { return m_buf; }
445 private:
446 char m_buf[64];
447 };
448
449 /// Indent each line of a string
450 extern std::string Indent( const char *s );
Indent(const std::string & s)451 inline std::string Indent( const std::string &s ) { return Indent( s.c_str() ); }
452
453 /// Generic hash
454 extern uint32 Murmorhash32( const void *data, size_t len );
455
456 /// Generate a fingerprint for a public that is reasonably collision resistant,
457 /// although not really cryptographically secure. (We are in charge of the
458 /// set of public keys and we expect it to be reasonably small.)
459 extern uint64 CalculatePublicKeyID( const CECSigningPublicKey &pubKey );
460 extern uint64 CalculatePublicKeyID_Ed25519( const void *pPubKey, size_t cbPubKey );
461
462 /// Check an arbitrary signature using the specified public key. (It's assumed that you have
463 /// already verified that this public key is from somebody you trust.)
464 extern bool BCheckSignature( const std::string &signed_data, CMsgSteamDatagramCertificate_EKeyType eKeyType, const std::string &public_key, const std::string &signature, SteamDatagramErrMsg &errMsg );
465
466 /// Parse PEM-like blob to a cert
467 extern bool ParseCertFromPEM( const void *pCert, size_t cbCert, CMsgSteamDatagramCertificateSigned &outMsgSignedCert, SteamNetworkingErrMsg &errMsg );
468 extern bool ParseCertFromBase64( const char *pBase64Data, size_t cbBase64Data, CMsgSteamDatagramCertificateSigned &outMsgSignedCert, SteamNetworkingErrMsg &errMsg );
469
470
IsPrivateIP(uint32 unIP)471 inline bool IsPrivateIP( uint32 unIP )
472 {
473 // RFC 1918
474 if ( ( unIP & 0xff000000 ) == 0x0a000000 ) // 10.0.0.0/8
475 return true;
476 if ( ( unIP & 0xfff00000 ) == 0xac100000 ) // 172.16.0.0/12
477 return true;
478 if ( ( unIP & 0xffff0000 ) == 0xc0a80000 ) // 192.168.0.0/16
479 return true;
480 return false;
481 }
482
483 extern const char *GetAvailabilityString( ESteamNetworkingAvailability a );
484
SteamNetworkingIPAddrToNetAdr(netadr_t & netadr,const SteamNetworkingIPAddr & addr)485 inline void SteamNetworkingIPAddrToNetAdr( netadr_t &netadr, const SteamNetworkingIPAddr &addr )
486 {
487 uint32 ipv4 = addr.GetIPv4();
488 if ( ipv4 )
489 netadr.SetIPv4( ipv4 );
490 else
491 netadr.SetIPV6( addr.m_ipv6 );
492 netadr.SetPort( addr.m_port );
493 }
494
NetAdrToSteamNetworkingIPAddr(SteamNetworkingIPAddr & addr,const netadr_t & netadr)495 inline void NetAdrToSteamNetworkingIPAddr( SteamNetworkingIPAddr &addr, const netadr_t &netadr )
496 {
497 netadr.GetIPV6( addr.m_ipv6 );
498 addr.m_port = netadr.GetPort();
499 }
500
AddrEqual(const SteamNetworkingIPAddr & s,const netadr_t & n)501 inline bool AddrEqual( const SteamNetworkingIPAddr &s, const netadr_t &n )
502 {
503 if ( s.m_port != n.GetPort() )
504 return false;
505 switch ( n.GetType() )
506 {
507 case k_EIPTypeV4:
508 return s.GetIPv4() == n.GetIPv4();
509
510 case k_EIPTypeV6:
511 return memcmp( s.m_ipv6, n.GetIPV6Bytes(), 16 ) == 0;
512 }
513
514 return false;
515 }
516
517 template <typename T>
NearestWithSameLowerBits(T nLowerBits,int64 nReference)518 inline int64 NearestWithSameLowerBits( T nLowerBits, int64 nReference )
519 {
520 COMPILE_TIME_ASSERT( sizeof(T) < sizeof(int64) ); // Make sure it's smaller than 64 bits, or else why are you doing this?
521 COMPILE_TIME_ASSERT( ~T(0) < 0 ); // make sure it's a signed type!
522 T nDiff = nLowerBits - T( nReference );
523 return nReference + nDiff;
524 }
525
526 /// Calculate hash of identity.
527 struct SteamNetworkingIdentityHash
528 {
529 uint32 operator()( const SteamNetworkingIdentity &x ) const;
530 };
531
IsValidSteamIDForIdentity(CSteamID steamID)532 inline bool IsValidSteamIDForIdentity( CSteamID steamID )
533 {
534 return steamID.GetAccountID() != 0 && ( steamID.BIndividualAccount() || steamID.BGameServerAccount() );
535 }
536
IsValidSteamIDForIdentity(uint64 steamid64)537 inline bool IsValidSteamIDForIdentity( uint64 steamid64 ) { return IsValidSteamIDForIdentity( CSteamID( steamid64 ) ); }
538
539 extern bool BSteamNetworkingIdentityToProtobufInternal( const SteamNetworkingIdentity &identity, std::string *strIdentity, CMsgSteamNetworkingIdentityLegacyBinary *msgIdentityLegacyBinary, SteamDatagramErrMsg &errMsg );
540 extern bool BSteamNetworkingIdentityToProtobufInternal( const SteamNetworkingIdentity &identity, std::string *strIdentity, std::string *bytesMsgIdentityLegacyBinary, SteamDatagramErrMsg &errMsg );
541 #define BSteamNetworkingIdentityToProtobuf( identity, msg, field_identity_string, field_identity_legacy_binary, field_legacy_steam_id, errMsg ) ( \
542 ( (identity).GetSteamID64() ? (void)(msg).set_ ## field_legacy_steam_id( (identity).GetSteamID64() ) : (void)0 ), \
543 BSteamNetworkingIdentityToProtobufInternal( identity, (msg).mutable_ ## field_identity_string(), (msg).mutable_ ## field_identity_legacy_binary(), errMsg ) \
544 )
545 #define SteamNetworkingIdentityToProtobuf( identity, msg, field_identity_string, field_identity_legacy_binary, field_legacy_steam_id ) \
546 { SteamDatagramErrMsg identityToProtobufErrMsg; \
547 if ( !BSteamNetworkingIdentityToProtobuf( identity, msg, field_identity_string, field_identity_legacy_binary, field_legacy_steam_id, identityToProtobufErrMsg ) ) { \
548 AssertMsg2( false, "Failed to serialize identity to %s message. %s", msg.GetTypeName().c_str(), identityToProtobufErrMsg ); \
549 } \
550 }
551
552 extern bool BSteamNetworkingIdentityFromLegacyBinaryProtobuf( SteamNetworkingIdentity &identity, const std::string &bytesMsgIdentity, SteamDatagramErrMsg &errMsg );
553 extern bool BSteamNetworkingIdentityFromLegacyBinaryProtobuf( SteamNetworkingIdentity &identity, const CMsgSteamNetworkingIdentityLegacyBinary &msgIdentity, SteamDatagramErrMsg &errMsg );
554 extern bool BSteamNetworkingIdentityFromLegacySteamID( SteamNetworkingIdentity &identity, uint64 legacy_steam_id, SteamDatagramErrMsg &errMsg );
555
556 template <typename TStatsMsg>
557 inline uint32 StatsMsgImpliedFlags( const TStatsMsg &msg );
558
559 template <typename TStatsMsg>
SetStatsMsgFlagsIfNotImplied(TStatsMsg & msg,uint32 nFlags)560 inline void SetStatsMsgFlagsIfNotImplied( TStatsMsg &msg, uint32 nFlags )
561 {
562 if ( ( nFlags & StatsMsgImpliedFlags( msg ) ) != nFlags )
563 msg.set_flags( nFlags );
564 else
565 msg.clear_flags(); // All flags we needed to send are implied by message, no need to send explicitly
566 }
567
568 // Returns:
569 // <0 Bad data
570 // 0 No data
571 // >0 OK
572 #define SteamNetworkingIdentityFromProtobuf( identity, msg, field_identity_string, field_identity_legacy_binary, field_legacy_steam_id, errMsg ) \
573 ( \
574 (msg).has_ ##field_identity_string() ? ( SteamNetworkingIdentity_ParseString( &(identity), sizeof(identity), (msg).field_identity_string().c_str() ) ? +1 : ( V_strcpy_safe( errMsg, "Failed to parse string" ), -1 ) ) \
575 : (msg).has_ ##field_identity_legacy_binary() ? ( BSteamNetworkingIdentityFromLegacyBinaryProtobuf( identity, (msg).field_identity_legacy_binary(), errMsg ) ? +1 : -1 ) \
576 : (msg).has_ ##field_legacy_steam_id() ? ( BSteamNetworkingIdentityFromLegacySteamID( identity, (msg).field_legacy_steam_id(), errMsg ) ? +1 : -1 ) \
577 : ( V_strcpy_safe( errMsg, "No identity data" ), 0 ) \
578 )
SteamNetworkingIdentityFromCert(SteamNetworkingIdentity & result,const CMsgSteamDatagramCertificate & msgCert,SteamDatagramErrMsg & errMsg)579 inline int SteamNetworkingIdentityFromCert( SteamNetworkingIdentity &result, const CMsgSteamDatagramCertificate &msgCert, SteamDatagramErrMsg &errMsg )
580 {
581 return SteamNetworkingIdentityFromProtobuf( result, msgCert, identity_string, legacy_identity_binary, legacy_steam_id, errMsg );
582 }
583
584 // NOTE: Does NOT check the cert signature!
585 extern int SteamNetworkingIdentityFromSignedCert( SteamNetworkingIdentity &result, const CMsgSteamDatagramCertificateSigned &msgCertSigned, SteamDatagramErrMsg &errMsg );
586
587 struct ConfigValueBase
588 {
589
590 // Config value we should inherit from, if we are not set
591 ConfigValueBase *m_pInherit = nullptr;
592
593 enum EState
594 {
595 kENotSet,
596 kESet,
597 kELocked,
598 };
599
600 // Is the value set?
601 EState m_eState = kENotSet;
602
IsLockedConfigValueBase603 inline bool IsLocked() const { return m_eState == kELocked; }
IsSetConfigValueBase604 inline bool IsSet() const { return m_eState > kENotSet; }
605
606 // Unlock, if we are locked
UnlockConfigValueBase607 inline void Unlock()
608 {
609 if ( m_eState == kELocked )
610 m_eState = kESet;
611 }
612 };
613
614 template<typename T>
615 struct ConfigValue : public ConfigValueBase
616 {
ConfigValueConfigValue617 inline ConfigValue() : m_data{} {}
ConfigValueConfigValue618 inline explicit ConfigValue( const T &defaultValue ) : m_data(defaultValue) { m_eState = kESet; }
619
620 T m_data;
621
622 /// Fetch the effective value
GetConfigValue623 inline const T &Get() const
624 {
625 const ConfigValueBase *p = this;
626 while ( !p->IsSet() )
627 {
628 Assert( p->m_pInherit );
629 p = p->m_pInherit;
630 }
631
632 const auto *t = static_cast<const ConfigValue<T> *>( p );
633 return t->m_data;
634 }
635
SetConfigValue636 inline void Set( const T &value )
637 {
638 Assert( !IsLocked() );
639 m_data = value;
640 m_eState = kESet;
641 }
642
643 // Lock in the current value
LockConfigValue644 inline void Lock()
645 {
646 if ( !IsSet() )
647 m_data = Get();
648 m_eState = kELocked;
649 }
650 };
651
652 template <typename T> struct ConfigDataTypeTraits {};
653 template <> struct ConfigDataTypeTraits<int32> { const static ESteamNetworkingConfigDataType k_eDataType = k_ESteamNetworkingConfig_Int32; };
654 template <> struct ConfigDataTypeTraits<int64> { const static ESteamNetworkingConfigDataType k_eDataType = k_ESteamNetworkingConfig_Int64; };
655 template <> struct ConfigDataTypeTraits<float> { const static ESteamNetworkingConfigDataType k_eDataType = k_ESteamNetworkingConfig_Float; };
656 template <> struct ConfigDataTypeTraits<std::string> { const static ESteamNetworkingConfigDataType k_eDataType = k_ESteamNetworkingConfig_String; };
657 template <> struct ConfigDataTypeTraits<void*> { const static ESteamNetworkingConfigDataType k_eDataType = k_ESteamNetworkingConfig_Ptr; };
658
659 struct GlobalConfigValueEntry
660 {
661 GlobalConfigValueEntry( ESteamNetworkingConfigValue eValue, const char *pszName, ESteamNetworkingConfigDataType eDataType, ESteamNetworkingConfigScope eScope, int cbOffsetOf );
662
663 ESteamNetworkingConfigValue const m_eValue;
664 const char *const m_pszName;
665 ESteamNetworkingConfigDataType const m_eDataType;
666 ESteamNetworkingConfigScope const m_eScope;
667 int const m_cbOffsetOf;
668 GlobalConfigValueEntry *m_pNextEntry;
669
670 union
671 {
672 int32 m_int32min;
673 float m_floatmin;
674 };
675 union
676 {
677 int32 m_int32max;
678 float m_floatmax;
679 };
680
681 // Types that do not support limits
682 template <typename T> void InitLimits( T _min, T _max ); // Intentionally not defined
683 template<typename T> inline void NoLimits() {}
684 template<typename T> inline void Clamp( T &val ) {}
685 };
686
687 // Types that do support clamping
688 template <> inline void GlobalConfigValueEntry::InitLimits<int32>( int32 _min, int32 _max ) { m_int32min = _min; m_int32max = _max; }
689 template<> void GlobalConfigValueEntry::NoLimits<int32>(); // Intentionally not defined
690 template<> inline void GlobalConfigValueEntry::Clamp<int32>( int32 &val ) { val = std::max( m_int32min, std::min( m_int32max, val ) ); }
691
692 template <> inline void GlobalConfigValueEntry::InitLimits<float>( float _min, float _max ) { m_floatmin = _min; m_floatmax = _max; }
693 template<> void GlobalConfigValueEntry::NoLimits<float>(); // Intentionally not defined
694 template<> inline void GlobalConfigValueEntry::Clamp<float>( float &val ) { val = std::max( m_floatmin, std::min( m_floatmax, val ) ); }
695
696 template<typename T>
697 struct GlobalConfigValueBase : GlobalConfigValueEntry
698 {
699 GlobalConfigValueBase( ESteamNetworkingConfigValue eValue, const char *pszName, ESteamNetworkingConfigScope eScope, int cbOffsetOf, const T &defaultValue )
700 : GlobalConfigValueEntry( eValue, pszName, ConfigDataTypeTraits<T>::k_eDataType, eScope, cbOffsetOf )
701 , m_value{defaultValue}
702 {
703 GlobalConfigValueEntry::NoLimits<T>();
704 }
705 GlobalConfigValueBase( ESteamNetworkingConfigValue eValue, const char *pszName, ESteamNetworkingConfigScope eScope, int cbOffsetOf, const T &defaultValue, const T &minVal, const T &maxVal )
706 : GlobalConfigValueEntry( eValue, pszName, ConfigDataTypeTraits<T>::k_eDataType, eScope, cbOffsetOf )
707 , m_value{defaultValue}
708 {
709 GlobalConfigValueEntry::InitLimits( minVal, maxVal );
710 }
711
712 inline const T &Get() const
713 {
714 DbgAssert( !m_value.m_pInherit );
715 DbgAssert( m_value.IsSet() );
716 return m_value.m_data;
717 }
718
719 struct Value : public ConfigValue<T>
720 {
721 inline Value( const T &defaultValue ) : ConfigValue<T>(defaultValue), m_defaultValue(defaultValue) {}
722 T m_defaultValue;
723 };
724 Value m_value;
725 };
726
727 template<typename T>
728 struct GlobalConfigValue : GlobalConfigValueBase<T>
729 {
730 GlobalConfigValue( ESteamNetworkingConfigValue eValue, const char *pszName, const T &defaultValue )
731 : GlobalConfigValueBase<T>( eValue, pszName, k_ESteamNetworkingConfig_Global, 0, defaultValue ) {}
732 GlobalConfigValue( ESteamNetworkingConfigValue eValue, const char *pszName, const T &defaultValue, const T &minVal, const T &maxVal )
733 : GlobalConfigValueBase<T>( eValue, pszName, k_ESteamNetworkingConfig_Global, 0, defaultValue, minVal, maxVal ) {}
734 };
735
736 struct ConnectionConfig
737 {
738 ConfigValue<int32> m_TimeoutInitial;
739 ConfigValue<int32> m_TimeoutConnected;
740 ConfigValue<int32> m_SendBufferSize;
741 ConfigValue<int32> m_SendRateMin;
742 ConfigValue<int32> m_SendRateMax;
743 ConfigValue<int32> m_MTU_PacketSize;
744 ConfigValue<int32> m_NagleTime;
745 ConfigValue<int32> m_IP_AllowWithoutAuth;
746 ConfigValue<int32> m_Unencrypted;
747 ConfigValue<int32> m_SymmetricConnect;
748 ConfigValue<int32> m_LocalVirtualPort;
749 ConfigValue<int64> m_ConnectionUserData;
750
751 ConfigValue<int32> m_LogLevel_AckRTT;
752 ConfigValue<int32> m_LogLevel_PacketDecode;
753 ConfigValue<int32> m_LogLevel_Message;
754 ConfigValue<int32> m_LogLevel_PacketGaps;
755 ConfigValue<int32> m_LogLevel_P2PRendezvous;
756
757 ConfigValue<void *> m_Callback_ConnectionStatusChanged;
758
759 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE
760 ConfigValue<std::string> m_P2P_STUN_ServerList;
761 ConfigValue<int32> m_P2P_Transport_ICE_Enable;
762 ConfigValue<int32> m_P2P_Transport_ICE_Penalty;
763 #endif
764
765 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR
766 ConfigValue<std::string> m_SDRClient_DebugTicketAddress;
767 ConfigValue<int32> m_P2P_Transport_SDR_Penalty;
768 #endif
769
770 void Init( ConnectionConfig *pInherit );
771 };
772
773 template<typename T>
774 struct ConnectionConfigDefaultValue : GlobalConfigValueBase<T>
775 {
776 ConnectionConfigDefaultValue( ESteamNetworkingConfigValue eValue, const char *pszName, int cbOffsetOf, const T &defaultValue )
777 : GlobalConfigValueBase<T>( eValue, pszName, k_ESteamNetworkingConfig_Connection, cbOffsetOf, defaultValue ) {}
778 ConnectionConfigDefaultValue( ESteamNetworkingConfigValue eValue, const char *pszName, int cbOffsetOf, const T &defaultValue, const T &minVal, const T &maxVal )
779 : GlobalConfigValueBase<T>( eValue, pszName, k_ESteamNetworkingConfig_Connection, cbOffsetOf, defaultValue, minVal, maxVal ) {}
780 };
781
782 extern GlobalConfigValue<float> g_Config_FakePacketLoss_Send;
783 extern GlobalConfigValue<float> g_Config_FakePacketLoss_Recv;
784 extern GlobalConfigValue<int32> g_Config_FakePacketLag_Send;
785 extern GlobalConfigValue<int32> g_Config_FakePacketLag_Recv;
786 extern GlobalConfigValue<float> g_Config_FakePacketReorder_Send;
787 extern GlobalConfigValue<float> g_Config_FakePacketReorder_Recv;
788 extern GlobalConfigValue<int32> g_Config_FakePacketReorder_Time;
789 extern GlobalConfigValue<float> g_Config_FakePacketDup_Send;
790 extern GlobalConfigValue<float> g_Config_FakePacketDup_Recv;
791 extern GlobalConfigValue<int32> g_Config_FakePacketDup_TimeMax;
792 extern GlobalConfigValue<int32> g_Config_PacketTraceMaxBytes;
793 extern GlobalConfigValue<int32> g_Config_FakeRateLimit_Send_Rate;
794 extern GlobalConfigValue<int32> g_Config_FakeRateLimit_Send_Burst;
795 extern GlobalConfigValue<int32> g_Config_FakeRateLimit_Recv_Rate;
796 extern GlobalConfigValue<int32> g_Config_FakeRateLimit_Recv_Burst;
797
798 extern GlobalConfigValue<int32> g_Config_EnumerateDevVars;
799 extern GlobalConfigValue<void*> g_Config_Callback_CreateConnectionSignaling;
800 extern ConnectionConfigDefaultValue<int32> g_ConfigDefault_LogLevel_PacketGaps;
801 extern ConnectionConfigDefaultValue<int32> g_ConfigDefault_LogLevel_P2PRendezvous;
802
803 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_STEAMNETWORKINGMESSAGES
804 extern GlobalConfigValue<void*> g_Config_Callback_MessagesSessionRequest;
805 extern GlobalConfigValue<void*> g_Config_Callback_MessagesSessionFailed;
806 #endif
807
808 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_SDR
809 extern GlobalConfigValue<int32> g_Config_SDRClient_ConsecutitivePingTimeoutsFailInitial;
810 extern GlobalConfigValue<int32> g_Config_SDRClient_ConsecutitivePingTimeoutsFail;
811 extern GlobalConfigValue<int32> g_Config_SDRClient_MinPingsBeforePingAccurate;
812 extern GlobalConfigValue<int32> g_Config_SDRClient_SingleSocket;
813 extern GlobalConfigValue<int32> g_Config_LogLevel_SDRRelayPings;
814 extern GlobalConfigValue<std::string> g_Config_SDRClient_ForceRelayCluster;
815 extern GlobalConfigValue<std::string> g_Config_SDRClient_ForceProxyAddr;
816 extern GlobalConfigValue<std::string> g_Config_SDRClient_FakeClusterPing;
817 #endif
818
819 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_ICE
820 extern ConnectionConfigDefaultValue< std::string > g_ConfigDefault_P2P_STUN_ServerList;
821 #endif
822
823 // This awkwardness (adding and subtracting sizeof(intptr_t)) silences an UBSan
824 // runtime error about "member access within null pointer"
825 #define V_offsetof(class, field) (int)((intptr_t)&((class *)(0+sizeof(intptr_t)))->field - sizeof(intptr_t))
826
827 #define DEFINE_GLOBAL_CONFIGVAL( type, name, ... ) \
828 GlobalConfigValue<type> g_Config_##name( k_ESteamNetworkingConfig_##name, #name, __VA_ARGS__ )
829 #define DEFINE_CONNECTON_DEFAULT_CONFIGVAL( type, name, ... ) \
830 ConnectionConfigDefaultValue<type> g_ConfigDefault_##name( k_ESteamNetworkingConfig_##name, #name, V_offsetof(ConnectionConfig, m_##name), __VA_ARGS__ )
831
832 inline bool RandomBoolWithOdds( float odds )
833 {
834 Assert( odds >= 0.0f && odds <= 100.0f );
835 if ( odds <= 0.0f )
836 return false;
837 return WeakRandomFloat( 0, 100.0 ) < odds;
838 }
839
840 } // namespace SteamNetworkingSocketsLib
841
842 #include <tier0/memdbgon.h>
843
844 // Set paranoia level, if not already set:
845 // 0 = disabled
846 // 1 = sometimes
847 // 2 = max
848 #ifndef STEAMNETWORKINGSOCKETS_SNP_PARANOIA
849 #ifdef _DEBUG
850 #define STEAMNETWORKINGSOCKETS_SNP_PARANOIA 2
851 #else
852 #define STEAMNETWORKINGSOCKETS_SNP_PARANOIA 0
853 #endif
854 #endif
855
856 #if ( STEAMNETWORKINGSOCKETS_SNP_PARANOIA > 0 ) && ( defined(__GNUC__ ) && defined( __linux__ ) && !defined( __ANDROID__ ) )
857 #define STEAMNETWORKINGSOCKETS_USE_GNU_DEBUG_MAP
858 #include <debug/map>
859 #endif
860
861 // Declare std_vector and std_map in our namespace. They use debug versions when available,
862 // a custom allocator
863 namespace SteamNetworkingSocketsLib
864 {
865
866 // Custom allocator that use malloc/free (and right now, those are #defines
867 // that go to our own functions if we are overriding memory allocation.)
868 #ifdef STEAMNETWORKINGSOCKETS_ENABLE_MEM_OVERRIDE
869 template <typename T>
870 struct Allocator
871 {
872 using value_type = T;
873 Allocator() noexcept = default;
874 template<class U> Allocator(const Allocator<U>&) noexcept {}
875 template<class U> bool operator==(const Allocator<U>&) const noexcept { return true; }
876 template<class U> bool operator!=(const Allocator<U>&) const noexcept { return false; }
877 static T* allocate( size_t n ) { return (T*)malloc( n *sizeof(T) ); }
878 static void deallocate( T *p, size_t n ) { free( p ); }
879 };
880 #else
881 template <typename T> using Allocator = std::allocator<T>;
882 #endif
883
884 #ifdef STEAMNETWORKINGSOCKETS_USE_GNU_DEBUG_MAP
885 // Use debug version of std::map
886 template< typename K, typename V, typename L = std::less<K> >
887 using std_map = __gnu_debug::map<K,V,L, Allocator< std::pair<const K, V> > >;
888 #else
889 template< typename K, typename V, typename L = std::less<K> >
890 using std_map = std::map<K,V,L,Allocator< std::pair<const K, V> >>;
891 #endif
892
893 template< typename T >
894 using std_vector = std::vector<T, Allocator<T> >;
895 }
896
897 //
898 // Some misc tools for using std::vector that our CUtlVector class had
899 //
900
901 template< typename I = int >
902 struct IndexRange
903 {
904 struct Iter
905 {
906 I i;
907 int operator*() const { return i; }
908 void operator++() { ++i; }
909 inline bool operator==( const Iter &x) const { return i == x.i; }
910 inline bool operator!=( const Iter &x) const { return i != x.i; }
911 };
912
913 I m_nBegin, m_nEnd;
914 Iter begin() const { return Iter{m_nBegin}; }
915 Iter end() const { return Iter{m_nEnd}; }
916 };
917
918 namespace vstd
919 {
920
921 template <typename V>
922 struct LikeStdVectorTraits {};
923
924 template <typename T, typename A>
925 struct LikeStdVectorTraits< std::vector<T,A> > { enum { yes=1 }; typedef T ElemType; };
926
927 }
928
929 template <typename V, typename I = int>
930 inline IndexRange<I> iter_indices( const V &vec )
931 {
932 (void)vstd::LikeStdVectorTraits<V>::yes;
933 return IndexRange<I>{ 0, (I)vec.size() };
934 }
935
936 template <typename V>
937 inline void erase_at( V &vec, int idx )
938 {
939 (void)vstd::LikeStdVectorTraits<V>::yes;
940 vec.erase( vec.begin()+idx );
941 }
942
943 template <typename V>
944 inline void pop_from_front( V &vec, int n )
945 {
946 (void)vstd::LikeStdVectorTraits<V>::yes;
947 auto b = vec.begin();
948 vec.erase( b, b+n );
949 }
950
951 template <typename V>
952 inline int push_back_get_idx( V &vec )
953 {
954 (void)vstd::LikeStdVectorTraits<V>::yes;
955 vec.resize( vec.size()+1 ); return int( vec.size()-1 );
956 }
957
958 template <typename V>
959 inline int push_back_get_idx( V &vec, const typename vstd::LikeStdVectorTraits<V>::ElemType &x )
960 {
961 vec.push_back( x ); return int( vec.size()-1 );
962 }
963
964 template <typename V>
965 inline typename vstd::LikeStdVectorTraits<V>::ElemType *push_back_get_ptr( V &vec )
966 {
967 vec.resize( vec.size()+1 ); return &vec[ vec.size()-1 ];
968 }
969
970 template <typename V>
971 inline typename vstd::LikeStdVectorTraits<V>::ElemType *push_back_get_ptr( V &vec, const typename vstd::LikeStdVectorTraits<V>::ElemType &x )
972 {
973 vec.push_back( x ); return &vec[ vec.size()-1 ];
974 }
975
976 // Return size as an *int*, not size_t, which is totally pedantic useless garbage in 99% of code.
977 template <typename V>
978 inline int len( const V &vec )
979 {
980 (void)vstd::LikeStdVectorTraits<V>::yes;
981 return (int)vec.size();
982 }
983
984 inline int len( const std::string &str )
985 {
986 return (int)str.length();
987 }
988
989 template <typename K, typename V, typename L, typename A>
990 inline int len( const std::map<K,V,L,A> &map )
991 {
992 return (int)map.size();
993 }
994
995 #ifdef STEAMNETWORKINGSOCKETS_USE_GNU_DEBUG_MAP
996 template <typename K, typename V, typename L, typename A>
997 inline int len( const __gnu_debug::map<K,V,L,A> &map )
998 {
999 return (int)map.size();
1000 }
1001 #endif
1002
1003 template <typename T, typename L, typename A>
1004 inline int len( const std::set<T,L,A> &map )
1005 {
1006 return (int)map.size();
1007 }
1008
1009 template< typename V>
1010 inline bool has_element( const V &vec, const typename vstd::LikeStdVectorTraits<V>::ElemType &x )
1011 {
1012 return std::find( vec.begin(), vec.end(), x ) != vec.end();
1013 }
1014
1015 template< typename V>
1016 inline bool find_and_remove_element( V &vec, const typename vstd::LikeStdVectorTraits<V>::ElemType &x )
1017 {
1018 auto iter = std::find( vec.begin(), vec.end(), x );
1019 if ( iter == vec.end() )
1020 return false;
1021 vec.erase( iter );
1022 return true;
1023 }
1024
1025 template< typename V>
1026 inline int index_of( const V &vec, const typename vstd::LikeStdVectorTraits<V>::ElemType &x )
1027 {
1028 int l = len( vec );
1029 for ( int i = 0 ; i < l ; ++i )
1030 {
1031 if ( vec[i] == x )
1032 return i;
1033 }
1034 return -1;
1035 }
1036
1037 namespace vstd
1038 {
1039
1040 template <typename T>
1041 void copy_construct_elements( T *dest, const T *src, size_t n )
1042 {
1043 if ( std::is_trivial<T>::value )
1044 {
1045 memcpy( dest, src, n*sizeof(T) );
1046 }
1047 else
1048 {
1049 T *dest_end = dest+n;
1050 while ( dest < dest_end )
1051 Construct<T>( dest++, *(src++) );
1052 }
1053 }
1054
1055 template <typename T>
1056 void move_construct_elements( T *dest, T *src, size_t n )
1057 {
1058 if ( std::is_trivial<T>::value )
1059 {
1060 memcpy( dest, src, n*sizeof(T) );
1061 }
1062 else
1063 {
1064 T *dest_end = dest+n;
1065 while ( dest < dest_end )
1066 Construct( dest++, std::move( *(src++) ) );
1067 }
1068 }
1069
1070 // Almost the exact same interface as std::vector, only it has a small initial capacity of
1071 // size N in a statically-allocated block of memory.
1072 //
1073 // The only difference between this and std::vector (aside from any missing functions that just
1074 // need to be written) is the guarantee about not constructing elements on swapping.
1075 template< typename T, int N >
1076 class small_vector
1077 {
1078 public:
1079 small_vector() {}
1080 small_vector( const small_vector<T,N> &x );
1081 small_vector<T,N> &operator=( const small_vector<T,N> &x );
1082 small_vector( small_vector<T,N> &&x );
1083 small_vector<T,N> &operator=( small_vector<T,N> &&x );
1084 ~small_vector() { clear(); }
1085
1086 size_t size() const { return size_; }
1087 size_t capacity() const { return capacity_; }
1088 bool empty() const { return size_ == 0; }
1089
1090 T *begin() { return dynamic_ ? dynamic_ : (T*)fixed_; };
1091 const T *begin() const { return dynamic_ ? dynamic_ : (T*)fixed_; };
1092
1093 T *end() { return begin() + size_; }
1094 const T *end() const { return begin() + size_; }
1095
1096 T &operator[]( size_t index ) { assert(index < size_); return begin()[index]; }
1097 const T &operator[]( size_t index ) const { assert(index < size_); return begin()[index]; }
1098
1099 void push_back( const T &value );
1100 void pop_back();
1101 void erase( T *it );
1102
1103 void resize( size_t n );
1104 void reserve( size_t n );
1105 void clear();
1106 void assign( const T *srcBegin, const T *srcEnd );
1107
1108 private:
1109 size_t size_ = 0, capacity_ = N;
1110 T *dynamic_ = nullptr;
1111 char fixed_[N][sizeof(T)];
1112 };
1113
1114 template<typename T, int N>
1115 small_vector<T,N>::small_vector( const small_vector<T,N> &x )
1116 {
1117 reserve( x.size_ );
1118 size_ = x.size_;
1119 vstd::copy_construct_elements<T>( begin(), x.begin(), x.size_ );
1120 }
1121
1122 template<typename T, int N>
1123 small_vector<T,N>::small_vector( small_vector<T,N> &&x )
1124 {
1125 size_ = x.size_;
1126 if ( x.dynamic_ )
1127 {
1128 capacity_ = x.capacity_;
1129 dynamic_ = x.dynamic_;
1130 x.dynamic_ = nullptr;
1131 x.size_ = 0;
1132 x.capacity_ = N;
1133 }
1134 else
1135 {
1136 vstd::move_construct_elements<T>( (T*)fixed_, (T*)x.fixed_, size_ );
1137 }
1138 }
1139
1140 template<typename T, int N>
1141 small_vector<T,N> &small_vector<T,N>::operator=( const small_vector<T,N> &x )
1142 {
1143 if ( this != &x )
1144 assign( x.begin(), x.end() );
1145 return *this;
1146 }
1147
1148 template<typename T, int N>
1149 small_vector<T,N> &small_vector<T,N>::operator=( small_vector<T,N> &&x )
1150 {
1151 clear();
1152 size_ = x.size_;
1153 if ( x.dynamic_ )
1154 {
1155 capacity_ = x.capacity_;
1156 dynamic_ = x.dynamic_;
1157 x.dynamic_ = nullptr;
1158 x.size_ = 0;
1159 x.capacity_ = N;
1160 }
1161 else
1162 {
1163 vstd::move_construct_elements<T>( (T*)fixed_, (T*)x.fixed_, size_ );
1164 }
1165 return *this;
1166 }
1167
1168 template< typename T, int N >
1169 void small_vector<T,N>::push_back( const T &value )
1170 {
1171 if ( size_ >= capacity_ )
1172 reserve( size_*2 + (63+sizeof(T))/sizeof(T) );
1173 Construct<T>( begin() + size_, value );
1174 ++size_;
1175 }
1176
1177 template< typename T, int N >
1178 void small_vector<T,N>::pop_back()
1179 {
1180 assert( size_ > 0 );
1181 --size_;
1182 ( begin() + size_ )->~T();
1183 }
1184
1185 template< typename T, int N >
1186 void small_vector<T,N>::erase( T *it )
1187 {
1188 T *e = end();
1189 assert( begin() <= it );
1190 assert( it < e );
1191
1192 if ( std::is_trivial<T>::value )
1193 {
1194 memmove( it, it+1, (char*)e - (char*)(it+1) );
1195 }
1196 else
1197 {
1198 --e;
1199 while ( it < e )
1200 {
1201 it[0] = std::move( it[1] );
1202 ++it;
1203 }
1204 e->~T();
1205 }
1206 --size_;
1207 }
1208
1209 template< typename T, int N >
1210 void small_vector<T,N>::reserve( size_t n )
1211 {
1212 if ( n <= capacity_ )
1213 return;
1214 assert( capacity_ >= size_ );
1215 if ( std::is_trivial<T>::value && dynamic_ )
1216 {
1217 dynamic_ = (T*)realloc( dynamic_, n * sizeof(T) );
1218 }
1219 else
1220 {
1221 T *new_dynamic = (T *)malloc( n * sizeof(T) );
1222 T *s = begin();
1223 T *e = s + size_;
1224 T *d = new_dynamic;
1225 while ( s < e )
1226 {
1227 Construct<T>( d, std::move( *s ) );
1228 s->~T();
1229 ++s;
1230 ++d;
1231 }
1232 if ( dynamic_ )
1233 ::free( dynamic_ );
1234 dynamic_ = new_dynamic;
1235 }
1236 capacity_ = n;
1237 }
1238
1239 template< typename T, int N >
1240 void small_vector<T,N>::resize( size_t n )
1241 {
1242 if ( n > size_ )
1243 {
1244 reserve( n );
1245 T *b = begin();
1246 while ( size_ < n )
1247 {
1248 Construct<T>( b ); // NOTE: Does not use value initializer, so PODs are *not* initialized
1249 ++b;
1250 ++size_;
1251 }
1252 }
1253 else
1254 {
1255 T *e = end();
1256 while ( size_ > n )
1257 {
1258 --size_;
1259 --e;
1260 e->~T();
1261 }
1262 }
1263 }
1264
1265 template< typename T, int N >
1266 void small_vector<T,N>::clear()
1267 {
1268 T *b = begin();
1269 T *e = b + size_;
1270 while ( e > b )
1271 {
1272 --e;
1273 e->~T();
1274 }
1275 if ( dynamic_ )
1276 {
1277 ::free( dynamic_ );
1278 dynamic_ = nullptr;
1279 }
1280 size_ = 0;
1281 capacity_ = N;
1282 }
1283
1284 template< typename T, int N >
1285 void small_vector<T,N>::assign( const T *srcBegin, const T *srcEnd )
1286 {
1287 if ( srcEnd <= srcBegin )
1288 {
1289 clear();
1290 return;
1291 }
1292 size_t n = srcEnd - srcBegin;
1293 if ( n > N )
1294 {
1295 // We need dynamic memory. If we're not exactly sized already,
1296 // just nuke everyhing we have.
1297 if ( n != capacity_ )
1298 {
1299 clear();
1300 reserve( n );
1301 }
1302 assert( dynamic_ );
1303 if ( !std::is_trivial<T>::value )
1304 {
1305 while ( size_ > n )
1306 dynamic_[--size_].~T();
1307 }
1308 }
1309 else if ( dynamic_ )
1310 {
1311 // We have dynamic allocation, but don't need it
1312 clear();
1313 }
1314 assert( capacity_ >= n );
1315 if ( std::is_trivial<T>::value )
1316 {
1317 // Just blast them over, and don't bother with the leftovers
1318 memcpy( begin(), srcBegin, n*sizeof(T) );
1319 }
1320 else
1321 {
1322 assert( size_ <= n );
1323
1324 // Complex type. Try to avoid excess constructor/destructor calls
1325 // First use operator= for items already constructed
1326 const T *s = srcBegin;
1327 T *d = begin();
1328 T *e = d + size_;
1329 while ( d < e && s < srcEnd )
1330 *(d++) = *(s++);
1331
1332 // Use copy constructor for any remaining items
1333 while ( s < srcEnd )
1334 Construct<T>( d++, *(s++) );
1335 }
1336 size_ = n;
1337 }
1338
1339 template <typename T,int N>
1340 struct LikeStdVectorTraits< small_vector<T,N> > { enum { yes = 1 }; typedef T ElemType; };
1341
1342 } // namespace vstd
1343
1344 #endif // STEAMNETWORKINGSOCKETS_INTERNAL_H
1345