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