This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions
+611
View File
@@ -0,0 +1,611 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#ifndef CLIENTENUMS_H
#define CLIENTENUMS_H
#ifdef _WIN32
#pragma once
#endif
enum ELogonState
{
k_ELogonStateNotLoggedOn = 0,
k_ELogonStateLoggingOn = 1,
k_ELogonStateLoggingOff = 2,
k_ELogonStateLoggedOn = 3
};
// Enums for all personal questions supported by the system.
enum EPersonalQuestion
{
// Never ever change these after initial release.
k_EPSMsgNameOfSchool = 0, // Question: What is the name of your school?
k_EPSMsgFavoriteTeam = 1, // Question: What is your favorite team?
k_EPSMsgMothersName = 2, // Question: What is your mother's maiden name?
k_EPSMsgNameOfPet = 3, // Question: What is the name of your pet?
k_EPSMsgChildhoodHero = 4, // Question: Who was your childhood hero?
k_EPSMsgCityBornIn = 5, // Question: What city were you born in?
k_EPSMaxPersonalQuestion
};
// account flags (stored in DB)
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASEd
enum EAccountFlags
{
m_EAccountFlagNormalUser = 0, // Standard user level (yes, this is meant to be zero)
k_EAccountFlagPersonaNameSet = ( 1 << 0 ), // true if the user has set the persona name they really want, instead of the auto-generated one
k_EAccountFlagUnbannable = ( 1 << 1 ), // whatever happens, this account can't be banned
k_EAccountFlagPasswordSet = ( 1 << 2 ), // we've set the password at least once on this account
k_EAccountFlagSupport = ( 1 << 3 ), // Enables use of web support tool
k_EAccountFlagAdmin = ( 1 << 4 ), // The name says it all, can do everything
k_EAccountFlagSupervisor = ( 1 << 5 ), // support supervisory role
k_EAccountFlagAppEditor = ( 1 << 6 ), // Can edit app info
k_EAccountFlagHWIDSet = ( 1 << 7 ), // Set HWID once
k_EAccountFlagPersonalQASet = ( 1 << 8 ), // user has personal Question & anser set
k_EAccountFlagVacBeta = ( 1 << 9 ), // user participates in VAC beta tests
k_EAccountFlagDebug = ( 1 << 10 ), // user is in debug mode, eg VAC doesn't kick etc
k_EAccountFlagDisabled = ( 1 << 11 ), // account is disabled.
k_EAccountFlagLimitedUser = ( 1 << 12 ), // account is limited user account because it doesnt own anything
k_EAccountFlagLimitedUserForce = ( 1 << 13 ), // account is limited user account because we forced it to be
k_EAccountFlagEmailValidated = ( 1 << 14 ), // user has verified email address via WG
k_EAccountFlagMarketingTreatment = ( 1 << 15), // account is flagged as being in a treatment for marketing/sales experiments
};
// profile state (stored in DB)
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ECommunityProfileState
{
k_ECommunityProfileNotCreated = 0, // user hasn't setup community account yet
k_ECommunityProfileActive = 1, // user joined community, site is public
k_ECommunityProfilePrivate = 2, // user joined community, site is private
k_ECommunityProfileLocked = 3, // user got locked, content can't be changed but is still accessible
k_ECommunityProfileDisabled = 4, // user got disabled, site not accessible anymore
};
// profile privacy option setting (stored in DB)
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ECommunityPrivacyState
{
k_ECommunityPrivacyInvalid = 0,
k_ECommunityPrivacyPrivate = 1, // ain't nobody can see it
k_ECommunityPrivacyFriendsOnly = 2, // only your friends can see it
k_ECommunityPrivacyPublic = 3, // anybody could see it
};
enum ECommunityVisibilityState
{
k_ECommunityVisibilityPrivate = 1, // private, requester see only public fields
k_ECommunityVisibilityFriendsOnly = 2, // friends only, requester sees only public fields
k_ECommunityVisibilityOpen = 3, // it is visible to requester; they are owner or friend or public
k_ECommunityVisibilitySupportPrivate = 4, // was private, but it's a support account asking
k_ECommunityVisibilitySupportFriendsOnly = 5,// was friends only, but it's a support account asking
};
enum ECommentPermission
{
k_ECommentPermissionFriendsOnly = 0, // only friends can leave a comment
k_ECommentPermissionAnyone = 1, // anybody can leave a comment
k_ECommentPermissionSelfOnly = 2, // only the account owner can leave a comment
};
// Payment methods for purchases - BIT FLAGS so can be used to indicate
// acceptable payment methods for packages
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum EPaymentMethod
{
k_EPaymentMethodNone = 0x000, // user got the license for free
k_EPaymentMethodActivationCode = 0x001, // user paid by entering unused CD-Key or other activation code
k_EPaymentMethodCreditCard = 0x002, // user paid with credit card
k_EPaymentMethodPayPal = 0x004, // user paid with via paypal
k_EPaymentMethodGuestPass = 0x008, // user paid by redeeming a guest pass
k_EPaymentMethodHardwarePromo = 0x010, // user presented machine credentials
k_EPaymentMethodClickAndBuy = 0x020, // ClickandBuy
k_EPaymentMethodAutoGrant = 0x040, // server side purchased package, things like German specific TF2 free weekend
k_EPaymentMethodWallet = 0x080, // user paid with wallet
k_EPaymentMethodOEMTicket = 0x100, // user paid by redeeming a OEM license ticket
k_EPaymentMethodSplit = 0x200, // user paid with wallet AND a provider
};
// Sources for WalletTxn records
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum EWalletSource
{
k_EWalletSourceInvalid = 0,
k_EWalletSourcePurchase = 1, // Created from a purchase, refund, chargeback, or reverse chargeback (PurchaseRefGID -> TransID)
k_EWalletSourceGuestPass = 2, // Created from a guest pass (PurchaseRefGID -> GuestPassID)
k_EWalletSourceConversion = 3, // Created from a wallet conversion (PurchaseRefGID -> GID shared between debit & credit records)
k_EWalletSourceRebate = 4, // Created from a rebate (PurchaseRefGID -> TransID)
};
// License types
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ELicenseType
{
k_ENoLicense = 0, // for shipped goods
k_ESinglePurchase = 1, // single purchase
k_ESinglePurchaseLimitedUse = 2, // single purchase w/ expiration
k_ERecurringCharge = 3, // recurring subscription
k_ERecurringChargeLimitedUse = 4, // recurring subscription w/ limited minutes per period
k_ERecurringChargeLimitedUseWithOverages = 5, // like above but w/ soft limit and overage charges
};
// Flags for licenses - BITS
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ELicenseFlags
{
k_ELicenseFlagNone = 0x00, // just a place holder
k_ELicenseFlagRenew = 0x01, // Renew this license next period
k_ELicenseFlagRenewalFailed = 0x02, // Auto-renew failed
k_ELicenseFlagPending = 0x04, // Purchase or renewal is pending
k_ELicenseFlagExpired = 0x08, // Set if no longer active (whatever the reason)
k_ELicenseFlagCancelledByUser = 0x10, // Cancelled by the user
k_ELicenseFlagCancelledByAdmin = 0x20, // Cancelled by customer support
k_ELicenseFlagLowViolenceContent = 0x40,// license is for low violence content
};
// Status of a package
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum EPackageStatus
{
k_EPackageAvailable = 0, // Available for purchase and use
k_EPackagePreorder = 1, // Available for purchase, as a pre-order
k_EPackageUnavailable = 2, // Not available for new purchases, may still be owned
k_EPackageInvalid = 3, // Either an unknown package or a deleted one that nobody should own
};
// Purchase status
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum EPurchaseStatus
{
k_EPurchasePending = 0, // purchase is pending, valid but pending subscription
k_EPurchaseSucceeded = 1, // purchase successful, valid subscription
k_EPurchaseFailed = 2, // purchase failed, no subscription
k_EPurchaseRefunded = 3, // we refunded the purchase and removed subscription
k_EPurchaseInit = 4, // user started purchase
k_EPurchaseChargedback = 5, // the user issued a chargeback, we removed subscription
k_EPurchaseRevoked = 6, // we revoked the purchase and removed subscription. Usually stolen CD-Keys
k_EPurchaseInDispute = 7, // the purchase is being disputed by the user, preliminary to a chargeback
};
// LineItemTypes
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASEd
enum ELineItemType
{
k_ELineItemTypeInvalid = 0, // Unknown - load all purchase line items
k_ELineItemTypeMicroTxn = ( 1 << 0 ), // Transaction has data in MicroTxnLineItem table
k_ELineItemTypeWallet = ( 1 << 1 ), // Transaction has data in WalletLineItem table
k_ELineItemTypePkg = ( 1 << 2 ), // Transaction has data in PurchaseLineItem table
};
// Enum for the types of news push items you can get
enum ENewsUpdateType
{
k_EAppNews = 0, // news about a particular app
k_ESteamAds = 1, // Marketing messages
k_ESteamNews = 2, // EJ's corner and the like
k_ECDDBUpdate = 3, // backend has a new CDDB for you to load
k_EClientUpdate = 4, // new version of the steam client is available
};
// Detailed purchase result codes for the client
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum EPurchaseResultDetail
{
k_EPurchaseResultNoDetail = 0,
k_EPurchaseResultAVSFailure = 1,
k_EPurchaseResultInsufficientFunds = 2,
k_EPurchaseResultContactSupport = 3,
k_EPurchaseResultTimeout = 4,
k_EPurchaseResultInvalidPackage = 5,
k_EPurchaseResultInvalidPaymentMethod = 6,
k_EPurchaseResultInvalidData = 7,
k_EPurchaseResultOthersInProgress = 8,
k_EPurchaseResultAlreadyPurchased = 9,
k_EPurchaseResultWrongPrice = 10,
k_EPurchaseResultFraudCheckFailed = 11,
k_EPurchaseResultCancelledByUser = 12,
k_EPurchaseResultRestrictedCountry = 13,
k_EPurchaseResultBadActivationCode = 14, // this code gives no receipt
k_EPurchaseResultDuplicateActivationCode = 15,
k_EPurchaseResultUseOtherPaymentMethod = 16, // User should try a different payment method
k_EPurchaseResultUseOtherFundingSource = 17, // Select a different funding source (paypal)
k_EPurchaseResultInvalidShippingAddress = 18, // Shipping address is invalid (paypal)
k_EPurchaseResultRegionNotSupported = 19, // This region is not supported with this payment type
k_EPurchaseResultAcctIsBlocked = 20, // Acct has been blocked by provider - user should contact provider to resolve
k_EPurchaseResultAcctNotVerified = 21, // Provider indicated account needs to be verified for transaction to complete
k_EPurchaseResultInvalidAccount = 22, // Provider indicated the account is invalid or no longer usable
k_EPurchaseResultStoreBillingCountryMismatch = 23, // store country code & billing country code do not match
k_EPurchaseResultDoesNotOwnRequiredApp = 24, // user does not own one of the apps required for purchase
k_EPurchaseResultCanceledByNewTransaction = 25, // user made a new transaction which canceled an old, pending transaction
k_EPurchaseResultForceCanceledPending = 26, // A pending transaction was force canceled (no response from provider)
k_EPurchaseResultFailCurrencyTransProvider = 27, // selected transaction provider does not support calculated currency
k_EPurchaseResultFailedCyberCafe = 28, // cybercafe account tried to purchase or use an activation code
k_EPurchaseResultNeedsPreApproval = 29, // Transaction needs approval from support
k_EPurchaseResultPreApprovalDenied = 30, // Transaction was denied by support
k_EPurchaseResultWalletCurrencyMismatch = 31, // Currency of purchase does not match currency of wallet
};
// Type of system IM. The client can use this to do special UI handling in specific circumstances
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ESystemIMType
{
k_ESystemIMRawText = 0,
k_ESystemIMInvalidCard = 1,
k_ESystemIMRecurringPurchaseFailed = 2,
k_ESystemIMCardWillExpire = 3,
k_ESystemIMSubscriptionExpired = 4,
k_ESystemIMGuestPassReceived = 5, // User has received a guest pass from a friend
k_ESystemIMGuestPassGranted = 6, // System has granted a user a guest pass to give out
k_ESystemIMGiftRevoked = 7, // We revoked a gift due to chargeback, etc
//
k_ESystemIMTypeMax
};
// Ways an external cd key can be munged onto a users PC
enum ELegacyKeyRegistrationMethod
{
eLegacyKeyRegistrationMethodNone = 0, // doesn't support legacy cd keys
eLegacyKeyRegistrationMethodRegistry, // just place it into the registry
eLegacyKeyRegistrationMethodDisk, // put it in a file on disk
};
// Support events, generated by system or support input
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ESupportEvent
{
// support activity
eSupportNote = 0, // a generic support note
eSupportLogin = 1, // support account logged in out, data: IP:Port
eSupportLogoff = 2, // support account logged out, text: IP:Port
eSupportTicketCreated = 3, // a support ticket was created
eSupportTicketClosed = 4, // problem was solved, ticket closed
// account changes
eSupportEnableAccount = 5, // support enabled account, text: reason
eSupportDisableAccount = 6, // support disabled account, text: reason
eSupportChangeAccountPassword = 7, // support or user changed password, data: DONT include old password
eSupportChangeAccountEmail = 8, // support or user changed email, data: old email
eSupportChangeAccountName = 9, // support or user changed name, data: old name
eSupportChangeAccountPlayer = 10, // support or user changed player name, data: old name
eSupportPurchaseChargedback = 11, // a charge back was issued
eSupportPurchaseRefunded = 12, // a refund was issued
eSupportPurchaseForcedCompletion = 13, // support forced a pending purchase to complete
// license handling
eSupportLicenseAdded = 14, // support added a license, text: reason
eSupportLicenseCanceled = 15, // support or user cancel a license
eSupportLicenseChanged = 16, // support removed a license, text: reason
eSupportBannedGame = 17, // support banned game for an account
eSupportUnbannedGame = 18, // support unbanned game for an account
// purchase activity
eSupportRunPurchase = 19,
eSupportChangedCreditCard = 20, // support updated a credit card
eSupportSetNoFraudCheckFlag = 21, // support disabled fraud check, data: reason
// banning
eSupportBannedCreditCard = 22, // support banned a credit card
eSupportBannedIP = 23, // support banned an IP
eSupportBannedCDKey = 24, // support banned an CDKey
eSupportBannedCountry = 25, // support banned a country
eSupportBannedPayPalAccount = 26,
eSupportPurchaseCanceled = 27, // support forced a pending purchase to cancel
eSupportChangeAvatar = 28,
eSupportChangeProfileURL = 29,
eSupportRegisterCDKey = 30, // support added a CD key to this account
eSupportGrantGuestPass = 31, // support granted a guest pass to this account
eSupportResubmitTransaction = 32, // support resubmitted a transaction
eSupportResetContent = 33, // reset user content based on abuse reports
eSupportLockProfile = 34, // temp block a user from modifying community content
eSupportSetCommunityState = 35, // perm lock a user profile, can't be modified
eSupportDeleteAbuseReports = 36, // deleted abuse reports for the SteamID
eSupportSetAccountFlags = 37, // changed account flags
eSupportChargebackStatusUpdate = 38, // support updated the pending chargeback status
eSupportRefundForcedCompletion = 39, // support forced a pending refund to complete
eSupportRefundCanceled = 40, // support forced a pending refund to cancel
eSupportRevokeActivationKey = 41, // support revoked and unlocked activation key
eSupportReverseChargeback = 42, // a charge back was reversed
eSupportRejectedActivationKey = 43, // activation key was rejected (invalid or already used)
eSupportPurchaseError = 44, // Purchase error
eSupportAudited = 45,
// items
eSupportBanItems = 46, // support banned a user's items
eSupportRestoreBannedItems = 47, // support restored banned items
eSupportRestoreDeletedItems = 48, // support restored deleted items
// comments
eSupportDeleteComments = 49, // cleared comments on this account (not written by this account)
eSupportDeleteItems = 50, // support deleted a user's items
eSupportDeleteCachedCard = 51, // support deleted a user's cached credit card
eSupportConvertedWallet = 52, // user's wallet was converted
eSupportTxnApproved = 53, // PreApproval granted
eSupportTxnDenied = 54, // PreApproval denied
eSupportGCAction = 55, // used for all support actions from the GC
};
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ESupportTicket
{
// all kinds of problems tickets that have to be handled by support
k_ETicketUnknown = 0, // an unknown problem. yay.
k_ETicketManual = 1, // a problem manually entered by support.
k_ETicketFraudRedFlag = 2, // fraud detection marked this account
k_ETicketPurchaseError = 3, // a purchase error happened
k_ETicketChargeback = 4, // a chargeback needs attention
};
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ESupportTicketState
{
// all kinds of problems tickets that have to be handled by support
k_ETicketStateUnknown = 0, // support issue state is unknown
k_ETicketStateUnassigned = 1, // support issue is 'open' but not assigned yet
k_ETicketStateInProcess = 2, // support issue is assigned to an support actor
k_ETicketStateResolved = 3, // support issue is fixed and closed
k_ETicketStateUnresolved = 4, // support issue couldn't be fixed. closed anyway
k_ETicketStateAutoClosed = 5, // System closed the ticket automatically
};
//-----------------------------------------------------------------------------
// types of content that can be reported as abused
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
//-----------------------------------------------------------------------------
enum ECommunityContentType
{
k_EContentUnspecified = 0,
k_EContentAll = 1, // reset all community content
k_EContentAvatarImage = 2, // clear avatar image
k_EContentProfileText = 3, // reset profile text
k_EContentWebLinks = 4, // delete web links
k_EContentAnnouncement = 5,
k_EContentEventText = 6,
k_EContentCustomCSS = 7,
k_EContentProfileURL = 8, // delete community URL ID
k_EContentComments = 9, // just comments this guy has written
};
//-----------------------------------------------------------------------------
// types of reasons why a violation report was issued
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
//-----------------------------------------------------------------------------
enum EAbuseReportType
{
k_EAbuseUnspecified = 0,
k_EAbuseInappropriate = 1, // just not ok to post
k_EAbuseProhibited = 2, // prohibited by EULA or general law
k_EAbuseSpamming = 3, // excessive spamming
k_EAbuseAdvertisement = 4, // unwanted advertisement
k_EAbuseExploit = 5, // content data attempts to exploit code issue
k_EAbuseSpoofing = 6, // user/group is impersonating an official contact
k_EAbuseLanguage = 7, // bad language
k_EAbuseAdultContent = 8, // any kind of adult material, references etc
k_EAbuseHarassment = 9, // harassment, discrimination, racism etc
};
//-----------------------------------------------------------------------------
// actions for a user within a clan for logging in the ClanHistory table
//-----------------------------------------------------------------------------
enum EClanAction
{
k_EJoined = 1, // joined the clan
k_ELeft = 2, // left the clan
k_EPromoted = 3, // promoted to officer
k_EDemoted = 4, // demoted from officer
k_EKicked = 5, // kicked off the clan
k_ECreated = 6, // clan was created
k_EInvited = 7, // invited someone
k_EEventCreated = 8, // clan event created
k_EEventUpdated = 9, // clan event updated
k_EEventDeleted = 10, // clan event deleted
k_EPermissionsChanged = 11, // clan permissions were changed
k_EAnnouncementCreated = 12, // clan announcement created
k_EAnnouncementUpdated = 13, // clan announcement updated
k_EAnnouncementDeleted = 14, // clan announcement deleted
k_EPOTWChanged = 15, // changed the POTW
k_ELinksChanged = 16, // links changed
k_EDetailsChanged = 17, // details changed
k_ESupportResetContent = 18, // support reset some or all of the clan content
k_ESupportLockedGroup = 19, // support locked this clan, it can't be modified anymore
k_ESupportUnlockedGroup = 20, // support unlocked this clan
k_ESupportChangedOwner = 21, // support transfered ownership
k_EMadePublic = 22, // made from private into public
k_EMadePrivate = 23, // made from public into private
k_ESupportDisabledGroup = 24, // support disabled this group, nobody can see it anymore
k_EKickedChat = 25, // kicked from chat
k_EBannedChat = 26, // banned from chat
k_EUnBannedChat = 27, // un-banned from chat
k_EHighestValidAction // keep me updated, please!
};
//-----------------------------------------------------------------------------
// types of events for use in the Clan Event Type table
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
//-----------------------------------------------------------------------------
enum EClanEventType
{
k_EOtherEvent = 1,
k_EGameEvent = 2,
k_EPartyEvent = 3,
k_EMeetingEvent = 4,
k_ESpecialCauseEvent = 5,
k_EMusicAndArtsEvent = 6,
k_ESportsEvent = 7,
k_ETripEvent = 8,
k_EChatEvent = 9,
k_EGameReleaseEvent = 10,
};
//-----------------------------------------------------------------------------
// types of marketing messages displayed to users
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
//-----------------------------------------------------------------------------
enum EMarketingMessageType
{
k_EMarketingMessageNowAvailable = 1,
k_EMarketingMessageWeekendDeal = 2,
k_EMarketingMessagePrePurchase = 3,
k_EMarketingMessagePlayNow = 4,
k_EMarketingMessagePreloadNow = 5,
k_EMarketingMessageGeneral = 6,
k_EMarketingMessageDemoQuit = 7,
k_EMarketingMessageGifting = 8,
k_EMarketingMessageEJsKorner = 9,
};
//-----------------------------------------------------------------------------
// types of associations a marketing message may have
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
//-----------------------------------------------------------------------------
enum EMarketingMessageAssociationType
{
k_EMarketingMessageNoAssociation = 0,
k_EMarketingMessageAppAssociation = 1,
k_EMarketingMessageSubscriptionAssociation = 2,
k_EMarketingMessagePublisherAssociation = 3,
k_EMarketingMessageGenreAssociation = 4,
};
//-----------------------------------------------------------------------------
// Marketing message visibility
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
//-----------------------------------------------------------------------------
enum EMarketingMessageVisibility
{
k_EMarketingMessageVisibleBeta = 1,
k_EMarketingMessageVisiblePublic = 2,
};
//-----------------------------------------------------------------------------
// Structures used in multiple messages
//-----------------------------------------------------------------------------
// Purchase message constants
// WARNING: Do not change these if an instance of this record may exist in a database!!!
// BUGBUG derrick - Since these also define schema, they should be moved into steamschema.h
const int k_cchCCNumMax = 16 + 1;
const int k_cchHolderNameMax = 100 + 1;
const int k_cchExpYearMax = 4 + 1;
const int k_cchExpMonthMax = 2 + 1;
const int k_cchCVV2Max = 4 + 1;
const int k_cchAddressMax = 128 + 1;
const int k_cchAddress2Max = k_cchAddressMax;
const int k_cchCityMax = 50 + 1;
const int k_cchPostcodeMax = 16 + 1;
const int k_cchStateMax = 32 + 1;
const int k_cchPhoneMax = 20 + 1;
const int k_cchEmailMax = 100 + 1;
const int k_cchCountryCodeMax = 2 + 1;
const int k_cchPayPalCheckoutTokenMax = 20 + 1;
const int k_cchStateCodeMax = 3 + 1;
const int k_cchCurrencyCodeMax = 3 + 1;
const int k_cubMaxDfsURL = 128; // Max size for URL descriptors on DFS
// License information
struct LicenseInfo_t
{
PackageId_t m_unPackageID;
RTime32 m_RTime32Created;
RTime32 m_RTime32NextProcess;
int32 m_nMinuteLimit;
int32 m_nMinutesUsed;
EPaymentMethod m_ePaymentMethod;
uint32 m_nFlags;
char m_rgchPurchaseCountryCode[k_cchCountryCodeMax];
int32 m_nTerritoryCode;
};
// Supported Currency Codes
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ECurrencyCode
{
k_ECurrencyCodeInvalid = 0,
k_ECurrencyCodeUSD = 1,
k_ECurrencyCodeGBP = 2,
k_ECurrencyCodeEUR = 3,
k_ECurrencyCodeMax = 4
};
enum ETaxType
{
k_ETaxTypeInvalid = 0,
k_ETaxTypeUSState = 1,
k_ETaxTypeVAT = 2
};
// client stat list
// needs to be kept in the same order, since it's part of the protocol
enum EClientStat
{
k_EClientStatP2PConnectionsUDP = 0,
k_EClientStatP2PConnectionsRelay = 1,
k_EClientStatP2PGameConnections = 2,
k_EClientStatP2PVoiceConnections = 3,
k_EClientStatBytesDownloaded = 4,
k_EClientStatMax, // must be last, used as array's of data
};
enum EP2PState
{
k_EP2PStateNotConnected,
k_EP2PStateUDP,
k_EP2PStateRelay,
};
// User response for authentication request
enum EMicroTxnAuthResponse
{
k_EMicroTxnAuthResponseInvalid = 0, // Invalid value
k_EMicroTxnAuthResponseAuthorize = 1, // user accepted microtransaction
k_EMicroTxnAuthResponseDeny = 2, // user denied microtransaction
k_EMicroTxnAuthResponseAutoDeny = 3, // client automatically denied microtransaction (user wasn't in game, etc.)
};
// Result of authorization request, returned to client
enum EMicroTxnAuthResult
{
k_EMicroTxnAuthResultInvalid = 0, // Invalid value
k_EMicroTxnAuthResultOK = 1, // Successfully authorized
k_EMicroTxnAuthResultFail = 2, // An error occurred
k_EMicroTxnAuthResultInsufficientFunds = 3, // User has insufficient funds to complete microtransaction
};
#endif
@@ -0,0 +1,33 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef IGAMECOORDINATOR_H
#define IGAMECOORDINATOR_H
#ifdef _WIN32
#pragma once
#endif
typedef uint32 AppId_t;
class CSteamID;
class IGameCoordinatorHost;
class IGCSQLResultSetList;
class IGameCoordinator
{
public:
virtual bool BInit( AppId_t unAppID, const char *pchAppPath, IGameCoordinatorHost *pHost ) = 0;
virtual bool BMainLoopOncePerFrame( uint64 ulLimitMicroseconds ) = 0;
virtual bool BMainLoopUntilFrameCompletion( uint64 ulLimitMicroseconds ) = 0;
virtual void Shutdown() = 0;
virtual void Uninit() = 0;
virtual void MessageFromClient( const CSteamID & senderID, uint32 unMsgType, void *pubData, uint32 cubData ) = 0;
virtual void Validate( CValidator &validator, const char *pchName ) = 0;
virtual void SQLResults( GID_t gidContextID ) = 0;
};
#define GAMECOORDINATOR_INTERFACE_VERSION "GAMECOORDINATOR003"
#endif // IGAMECOORDINATOR_H
@@ -0,0 +1,31 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Provides an interface that the server hosting a GC must implement
//
// $NoKeywords: $
//=============================================================================
#ifndef IGAMECOORDINATORHOST_H
#define IGAMECOORDINATORHOST_H
#ifdef _WIN32
#pragma once
#endif
class CSteamID;
class IGCSQLQuery;
class IGameCoordinatorHost
{
public:
virtual bool BSendMessageToClient( AppId_t unAppID, const CSteamID & steamIDTarget, uint32 unMsgType, const void *pubData, uint32 cubData ) = 0;
virtual GID_t GenerateGID() = 0;
virtual void EmitMessage( const char *pchGroupName, SpewType_t spewType, int iSpewLevel, int iLevelLog, const char *pchMsg ) = 0;
virtual void SQLQuery( GID_t gidContextID, IGCSQLQuery *pQuery, int eSchemaCatalog ) = 0;
virtual void StartupComplete( bool bSuccess ) = 0;
virtual void ShutdownComplete() = 0;
virtual EUniverse GetUniverse() = 0;
};
#define GAMECOORDINATORHOST_INTERFACE_VERSION "GAMECOORDINATORHOST002"
#endif // IGAMECOORDINATORHOST_H
@@ -0,0 +1,67 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#ifndef IGCSQLQUERY_H
#define IGCSQLQUERY_H
#ifdef _WIN32
#pragma once
#endif
// the type of the parameter
enum EGCSQLType
{
k_EGCSQLTypeInvalid = -1,
// Variable length types
k_EGCSQLType_Blob,
k_EGCSQLType_String,
// fixed length types
k_EGCSQLType_int8, // also uint8
k_EGCSQLType_int16, // also uint16
k_EGCSQLType_int32, // also uint32
k_EGCSQLType_int64, // also uint64
k_EGCSQLType_float,
k_EGCSQLType_double,
k_EGCSQLType_Binary, // raw binary data of fixed size (i.e. a C struct).
k_EGCSQLType_Image,
k_EGCSQLType_bool,
};
class IGCSQLResultSetList;
class IGCSQLQuery
{
protected:
// call Destroy() instead of deleting this object directly
virtual ~IGCSQLQuery() {}
public:
// returns the number of statements in the transaction
// represented by this query object
virtual uint32 GetStatementCount() = 0;
// returns a string that represents where in the GC this
// query came from. Usually this is FILE_AND_LINE.
virtual const char *PchName() = 0;
// get the null-terminated query string itself
virtual const char *PchCommand( uint32 unStatement ) = 0;
// gets the parameter data
virtual uint32 CnParams( uint32 unStatement ) = 0;
virtual EGCSQLType EParamType( uint32 unStatement, uint32 uIndex ) = 0;
virtual byte *PubParam( uint32 unStatement, uint32 uIndex ) = 0;
virtual uint32 CubParam( uint32 unStatement, uint32 uIndex ) = 0;
// reports the result
virtual void SetResults( IGCSQLResultSetList *pResults ) = 0;
};
#endif // IGCSQLQUERY_H
@@ -0,0 +1,62 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#ifndef IGCSQLRESULTSETLIST_H
#define IGCSQLRESULTSETLIST_H
#ifdef _WIN32
#pragma once
#endif
#include "igcsqlquery.h"
enum EGCSQLError
{
k_EGCSQLErrorNone = 0,
k_EGCSQLErrorUnknown,
k_EGCSQLErrorBacklog,
k_EGCSQLErrorBadQueryParameters,
k_EGCSQLErrorConnectionError,
k_EGCSQLErrorDataTruncated,
k_EGCSQLErrorDeadlockLoser,
k_EGCSQLErrorDuplicateKey,
k_EGCSQLErrorGenericError,
k_EGCSQLErrorNoResultSet,
k_EGCSQLErrorSyntaxError,
k_EGCSQLErrorTableOrViewNotFound,
k_EGCSQLErrorTimeout,
k_EGCSQLErrorConstraintViolation,
k_EGCSQLErrorNumericValueOutOfRange,
k_EGCSQLErrorRollbackFailed,
k_EGCSQLErrorColumnNotFound,
};
class IGCSQLResultSet
{
public:
virtual uint32 GetColumnCount() = 0;
virtual EGCSQLType GetColumnType( uint32 nColumn ) = 0;
virtual const char *GetColumnName( uint32 nColumn ) = 0;
virtual uint32 GetRowCount() = 0;
virtual bool GetData( uint32 unRow, uint32 unColumn, uint8 **ppData, uint32 *punSize ) = 0;
};
class IGCSQLResultSetList
{
public:
virtual EGCSQLError GetError() = 0;
virtual uint32 GetResultSetCount() = 0;
virtual IGCSQLResultSet *GetResultSet( uint32 nResultSetIndex ) = 0;
virtual uint32 GetRowsAffected( uint32 unWhichStatement ) = 0;
virtual void Destroy() = 0;
virtual const char *GetErrorText() = 0;
};
#endif // IGCSQLRESULTSETLIST_H
+187
View File
@@ -0,0 +1,187 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Miscellaneous code
//
//=============================================================================
#include "stdafx.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Purpose: Tells us whether an account name looks like a VTT account name
// (used as an exception for IP-based rate limiting)
//-----------------------------------------------------------------------------
bool IsVTTAccountName( const char *szAccountName )
{
const static char *k_szCafe = "valvecafepc";
if ( 0 == Q_strncmp( szAccountName, k_szCafe, Q_strlen( k_szCafe ) ) )
return true;
return false;
}
//-----------------------------------------------------------------------------
// Random number generation
// Use this system for all random number generation. It's very fast, and is
// integrated with our automated tests so that we can perform reproducibly "random"
// test cases.
//-----------------------------------------------------------------------------
uint32 g_unRandCur = 0;
int g_iunRandMask = 0;
// k_rgunMask
// Set of masks used by our quick and dirty random number generator.
const uint32 k_rgunMask[17] =
{
0x1739a3b0,
0xb8907fe1,
0x8290d3b7,
0x72839cd0,
0x242df096,
0x3829750b,
0x38de7a77,
0x72f0924c,
0x44783927,
0x01925372,
0x20902714,
0x27585920,
0x27890632,
0x82910476,
0x72906721,
0x28798904,
0x78592700,
};
//-----------------------------------------------------------------------------
// Purpose: Quickly generates a vaguely random number.
// Output: A vaguely random number.
//-----------------------------------------------------------------------------
uint32 UNRandFast()
{
g_iunRandMask++;
g_unRandCur += 637429601; // Just add a large prime number (we'll wrap frequently)
return ( g_unRandCur ^ k_rgunMask[ g_iunRandMask % 17 ] );
}
//-----------------------------------------------------------------------------
// Purpose: Quickly generates a vaguely random character.
// Output: A vaguely random char in the range [32,126].
//-----------------------------------------------------------------------------
char CHRandFast()
{
return ( UNRandFast() % 95 ) + 32;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the random number seed (note that we actually break this down
// into two parts: g_unRandCur and g_iunRandMask).
// Input: ulRandSeed: Value to use as our seed
//-----------------------------------------------------------------------------
void SetRandSeed( uint64 ulRandSeed )
{
g_unRandCur = ulRandSeed >> 32;
g_iunRandMask = ulRandSeed & 0xffffffff;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the current random number seed (actually a composite of
// g_unRandCur and g_iunRandMask)
// Output: Our current 64 bit random number seed.
//-----------------------------------------------------------------------------
uint64 GetRandSeed()
{
return ( ( ((uint64)g_unRandCur) << 32 ) + g_iunRandMask );
}
//-----------------------------------------------------------------------------
// Purpose: Quickly fill a memory block with random bytes
//-----------------------------------------------------------------------------
void RandMem(void *dest, int count)
{
unsigned char *pDest = (unsigned char *)dest;
while ( count >= 4 )
{
*(uint32*)(pDest) = UNRandFast();
pDest+=4;
count-=4;
}
while ( count > 0 )
{
*pDest = UNRandFast();
pDest++;
count--;
}
}
//-----------------------------------------------------------------------------
// Purpose: Calculates the percentage of numerator/demoninator, or 0 if
// denominator is 0.
//-----------------------------------------------------------------------------
float SafeCalcPct( uint64 ulNumerator, uint64 ulDenominator )
{
if ( 0 == ulDenominator )
return 0;
return ( 100.0f * (float) ulNumerator / (float) ulDenominator );
}
//-----------------------------------------------------------------------------
// Purpose: Common code to reject an operation due to a time backlog.
// Does a gradual fade of 0% rejections at the specified threshold
// up to 100% at the limit. The gradual fade reduces system oscillations
// that could occur if you abruptly stop allowing all operations.
// Input: nBacklogCur - the current backlog (in arbitrary units)
// nBacklogThreshold - the threshold backlog at which to begin rejecting
// nBacklogLimit - hard limit at which to reject 100% of operations
// iItem - a monotonically increasing counter of items submitted. Used
// to determine which operations are allowed if there is a partial
// rejection rate.
//-----------------------------------------------------------------------------
bool BRejectDueToBacklog( int nBacklogCur, int nBacklogThreshold, int nBacklogLimit, int iItem )
{
bool bRefuse = false;
if ( nBacklogCur >= nBacklogLimit )
{
// if we're over the hard backlog limit, refuse all operations
bRefuse = true;
}
else if ( nBacklogCur >= nBacklogThreshold )
{
// if we're near the hard backlog limit, start refusing an increasing % of operations,
// so we don't snap abruptly in and out of accepting operations and potentially cause oscillations
// ramp from refusing 0% of operations at the backlog threshold up to 100% at the backlog hard limit
// calculate refuse % to nearest 10%, to make it easy to mod the item # and get a good distribution
float nRefusePctDecile = 10 * (float) ( nBacklogCur - nBacklogThreshold ) /
(float) ( nBacklogLimit - nBacklogThreshold );
Assert( nRefusePctDecile >= 0.0 );
Assert( nRefusePctDecile <= 10.0 );
// compare the operations submitted count mod 10 to the refusal percent decile to decide if we should
// accept or refuse this particular operation
if ( ( iItem % 10 ) < nRefusePctDecile )
bRefuse = true;
}
return bRefuse;
}
//-----------------------------------------------------------------------------
// Purpose: Defines the head of the CDumpMemFnReg linked list
//-----------------------------------------------------------------------------
CDumpMemFnReg *CDumpMemFnReg::sm_Head = NULL;
+121
View File
@@ -0,0 +1,121 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Miscellaneous platform-specific code
//
//=============================================================================
#ifndef MISC_H
#define MISC_H
// Random number utilities
uint32 UNRandFast();
char CHRandFast();
void SetRandSeed( uint64 ulRandSeed );
uint64 GetRandSeed();
void RandMem(void *dest, int count);
bool IsVTTAccountName( const char *szAccountName );
float SafeCalcPct( uint64 ulNumerator, uint64 ulDenominator );
bool BRejectDueToBacklog( int nBacklogCur, int nBacklogThreshold, int nBacklogLimit, int iItem );
#define SAFE_CLOSE_HANDLE( x ) if ( INVALID_HANDLE_VALUE != ( x ) ) { CloseHandle( x ); ( x ) = INVALID_HANDLE_VALUE; }
#define SAFE_DELETE( x ) if ( NULL != ( x ) ) { delete ( x ); ( x ) = NULL; }
#define SAFE_CLOSE_HCONNECTION( x ) if ( 0 != ( x ) ) { CNet::BClose( ( x ) ); ( x ) = 0; }
#define SAFE_RELEASE( x ) if ( NULL != ( x ) ) { ( x )->Release(); x = NULL; }
#define DECLARE_STEAM_CLASS_SIMPLE( className, baseClassName ) \
typedef baseClassName BaseClass; \
typedef className ThisClass; \
#define DECLARE_STEAM_CLASS_NOBASE( className ) \
typedef className ThisClass; \
// Type for our memory output debugging.
class IDisplayMemPoolStats
{
public:
virtual void Display( const char *pszClassName, uint64 ulClassSize, uint32 unPoolInstanceCount, uint64 ulPoolMemoryUsage, uint32 unPoolPeakInstanceCount, uint64 ulPoolPeakMemoryUsage ) = 0;
};
// A class for registering functions that will print memory usage information
typedef void (*DumpMemFn_t)( IDisplayMemPoolStats * );
class CDumpMemFnReg
{
public:
static CDumpMemFnReg *sm_Head;
CDumpMemFnReg( DumpMemFn_t fn )
: m_fn( fn ),
m_pNext( sm_Head )
{
sm_Head = this;
}
DumpMemFn_t m_fn;
CDumpMemFnReg *m_pNext;
};
// Helper macros for creating and using CClassMemoryPool on our frequently allocated objects
#define DECLARE_CLASS_MEMPOOL( className ) \
private: \
static CUtlMemoryPool sm_classMemPool; \
public: \
static void* operator new ( size_t nSize ); \
static void* operator new ( size_t nSize, int nBlockUse, const char *pFileName, int nLine ); \
static void operator delete ( void *pMem ); \
static void DumpMemStats( IDisplayMemPoolStats *pDisplayer ); \
#define IMPLEMENT_CLASS_MEMPOOL( className, initSize, growMode ) \
CUtlMemoryPool className::sm_classMemPool( sizeof( className ), ( initSize ), ( growMode ), MEM_ALLOC_CLASSNAME( className ) ); \
\
void* className::operator new ( size_t nSize ) \
{ \
if ( nSize != sizeof( className ) ) \
{ \
EmitError( SPEW_CONSOLE, #className"::operator new() called on wrong size! Expected %llu, Got %llu\n", (uint64)sizeof( className ), (uint64)nSize ); \
return NULL; \
} \
\
return sm_classMemPool.Alloc(); \
} \
\
void* className::operator new ( size_t nSize, int nBlockUse, const char *pFileName, int nLine ) \
{ \
if ( nSize != sizeof( className ) ) \
{ \
EmitError( SPEW_CONSOLE, #className"::operator new() called on wrong size! Expected %llu, Got %llu\n", (uint64)sizeof( className ), (uint64)nSize ); \
return NULL; \
} \
\
return sm_classMemPool.Alloc(); \
} \
\
void className::operator delete ( void *pMem ) \
{ \
sm_classMemPool.Free( (className *)pMem ); \
} \
\
void className::DumpMemStats( IDisplayMemPoolStats *pDisplayer ) \
{ \
Assert( pDisplayer ); \
pDisplayer->Display \
( \
#className, \
sizeof( className ), \
sm_classMemPool.Count(), \
sm_classMemPool.Count() * sizeof( className ), \
( ( sm_classMemPool.PeakCount() + ( initSize ) ) / ( initSize ) ) * ( initSize ) , \
( ( sm_classMemPool.PeakCount() + ( initSize ) ) / ( initSize ) ) * ( initSize ) * sizeof( className ) \
); \
} \
\
static CDumpMemFnReg s_##className##RegDumpMemory( &className::DumpMemStats );
// useful macro for rendering an IP
#define iptod(x) ((x)>>24&0xff), ((x)>>16&0xff), ((x)>>8&0xff), ((x)&0xff)
#endif // MISC_H
File diff suppressed because it is too large Load Diff
+187
View File
@@ -0,0 +1,187 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Encapsulates real world (wall clock) time
//
//=============================================================================
#ifndef RTIME_H
#define RTIME_H
#ifdef _WIN32
#pragma once
#endif
#ifdef WIN32
char* strptime(const char *s, const char *format, struct tm *tm);
#endif
#include <time.h>
#include <ctype.h>
#include <string.h>
class CSTime;
// Invalid time values
const RTime32 k_RTime32Nil = 0;
// time values between Nil and MinValid are available for special constants
const RTime32 k_RTime32MinValid = 10;
// infinite time value
const RTime32 k_RTime32Infinite = 0x7FFFFFFF; //01-18-2038
// Render buffer size
const size_t k_RTimeRenderBufferSize = 25;
// Flags for component fields. Longer durations must be > shorter ones
// WARNING: DO NOT RENUMBER EXISTING VALUES - STORED IN DATABASE
enum ETimeUnit
{
k_ETimeUnitNone = 0,
k_ETimeUnitSecond = 1,
k_ETimeUnitMinute = 2,
k_ETimeUnitHour = 3,
k_ETimeUnitDay = 4,
k_ETimeUnitWeek = 5,
k_ETimeUnitMonth = 6,
k_ETimeUnitYear = 7,
k_ETimeUnitForever
};
// CRTime
// This is our primary real time structure.
// It offers 1 second resolution beginning on January 1, 1970 (i.e unix time)
// This represents wall clock time
class CRTime
{
public:
// default constructor initializes to the current time
CRTime();
CRTime( RTime32 nTime ) : m_nStartTime( nTime ), m_bGMT( false ) {}
void SetToCurrentTime() { m_nStartTime = sm_nTimeCur; }
void SetFromCurrentTime( int dSecOffset ) { m_nStartTime = sm_nTimeCur + dSecOffset; }
// Amount of seconds that have passed between this CRTime being set and the current wall clock time.
int CSecsPassed() const;
// Time accessors
RTime32 GetRTime32() const { return m_nStartTime; }
void SetRTime32( RTime32 rTime32 ) { m_nStartTime = rTime32; }
// Access our cached current time value
static void UpdateRealTime();
static void SetSystemClock( RTime32 nCurrentTime );
static RTime32 RTime32TimeCur() { return sm_nTimeCur; }
// Render
const char *Render( char (&buf)[k_RTimeRenderBufferSize] ) const;
static const char *Render( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize] );
// Return a representation of the current system time
static char *PchTimeCur() { return sm_rgchLocalTimeCur; }
// Return a representation of the current system date
static char *PchDateCur() { return sm_rgchLocalDateCur; }
// comparisons against other CRTime objects
bool operator==( const CRTime &val ) const { return val.m_nStartTime == m_nStartTime; }
bool operator<( const CRTime &val ) const { return m_nStartTime < val.m_nStartTime; }
bool operator<=( const CRTime &val ) const { return m_nStartTime <= val.m_nStartTime; }
bool operator>( const CRTime &val ) const { return m_nStartTime > val.m_nStartTime; }
bool operator>=( const CRTime &val ) const { return m_nStartTime >= val.m_nStartTime; }
// comparisons against RTime32 numbers (to avoid implicit construct/destruct of a temporary CRTime)
bool operator==( const RTime32 &val ) const { return m_nStartTime == val; }
bool operator<( const RTime32 &val ) const { return m_nStartTime < val; }
bool operator<=( const RTime32 &val ) const { return m_nStartTime <= val; }
bool operator>( const RTime32 &val ) const { return m_nStartTime > val; }
bool operator>=( const RTime32 &val ) const { return m_nStartTime >= val; }
const CRTime& operator=( const RTime32 &val ) { m_nStartTime = val; return *this; }
const CRTime& operator=( const CRTime &val ) { m_nStartTime = val.m_nStartTime; return *this; }
const CRTime operator+( const int &nVal ) { return m_nStartTime + nVal; }
// Add exactly X seconds to this time
const CRTime& operator+=( const int &nVal ) { m_nStartTime += nVal; return *this; }
// Component Details
int GetYear() const;
int GetMonth() const; // returns 0..11
int GetDayOfYear() const;
int GetDayOfMonth() const;
int GetDayOfWeek() const;
int GetHour() const;
int GetMinute() const;
int GetSecond() const;
int GetISOWeekOfYear() const;
// Handy references to nearby time boundaries
static RTime32 RTime32BeginningOfDay( const RTime32 ); // at 00:00:00
static RTime32 RTime32BeginningOfNextDay( const RTime32 ); // at 00:00:00
static RTime32 RTime32FirstDayOfMonth( const RTime32 ); // at 00:00:00
static RTime32 RTime32LastDayOfMonth( const RTime32 ); // at 00:00:00
static RTime32 RTime32FirstDayOfNextMonth( const RTime32 ); // at 00:00:00
static RTime32 RTime32LastDayOfNextMonth( const RTime32 ); // at 00:00:00
static bool BIsLeapYear( int nYear );
bool BIsLeapYear() const { return CRTime::BIsLeapYear( GetYear() ); }
// Parse time using a format string with strptime format
static RTime32 RTime32FromFmtString( const char *pchFmt, const char* pchValue );
// Parse time from string in standard HTTP date format
static RTime32 RTime32FromHTTPDateString( const char* pchValue );
// Parse time from string RFC3339 format (assumes UTC)
static RTime32 RTime32FromRFC3339UTCString( const char* pchValue );
static const char* RTime32ToRFC3339UTCString( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize] );
// parse time from string "YYYY-MM-DD hh:mm:ss" or just "YYYY-MM-DD", the ' ',':','-' are optional
static RTime32 RTime32FromString( const char* pszValue );
// turns RTime32 in a string like "YYYY-MM-DD hh:mm:ss"
static const char* RTime32ToString( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize], bool bNoPunct = false, bool bOnlyDate = false );
// turns RTime32 into a string like "Aug 21"
static const char* RTime32ToDayString( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize], bool bGMT = false );
// If the month only has K days, K < N, returns Kth day
static RTime32 RTime32NthDayOfMonth( const RTime32, int nDay ); // at 00:00:00
// Add X months but return the Nth day of that month. If the month only has K days, K < N, returns Kth day.
static RTime32 RTime32MonthAddChooseDay( int nNthDayOfMonth, RTime32 rtime32StartDate, int nMonthsToAdd );
RTime32 GetBeginningOfDay() const { return RTime32BeginningOfDay( m_nStartTime ); }
RTime32 GetBeginningOfNextDay() const { return RTime32BeginningOfNextDay( m_nStartTime ); }
RTime32 GetFirstDayOfMonth() const { return RTime32FirstDayOfMonth( m_nStartTime ); }
RTime32 GetLastDayOfMonth() const { return RTime32LastDayOfMonth( m_nStartTime ); }
RTime32 GetFirstDayOfNextMonth() const { return RTime32FirstDayOfNextMonth( m_nStartTime ); }
RTime32 GetLastDayOfNextMonth() const { return RTime32LastDayOfNextMonth( m_nStartTime ); }
RTime32 GetNthDayOfMonth( int nDay ) const { return RTime32NthDayOfMonth( m_nStartTime, nDay ); }
RTime32 MonthAddChooseDay( int nNthDayOfMonth, int nMonthsToAdd ) const { return RTime32MonthAddChooseDay( nNthDayOfMonth, m_nStartTime, nMonthsToAdd ); }
// Add or subtract N days, weeks, minutes, etc from the current time
static RTime32 RTime32DateAdd( const RTime32, int nAmount, ETimeUnit eTimeAmountType );
RTime32 DateAdd( int nAmount, ETimeUnit eTimeAmountType ) const { return RTime32DateAdd( m_nStartTime, nAmount, eTimeAmountType ); }
// Compare two times, and return what the largest time boundary crossed between the two was.
// Week boundaries do not line up with Month or Year boundaries, so you must rely on pbWeekChanged for them!
static ETimeUnit FindTimeBoundaryCrossings( RTime32 unTime1, RTime32 unTime2, bool *pbWeekChanged );
void SetToGMT( bool bUseGMT ) { m_bGMT = bUseGMT;}
bool BIsGMT() const { return m_bGMT; }
private:
// prevent assignment or copy construction from the server time type
const CRTime& operator=( const CSTime &val ) { return *this; }
CRTime( const CSTime& ) {}
RTime32 m_nStartTime; // the time store by this instance (wall clock, in seconds)
static RTime32 sm_nTimeCur; // current system wide wall clock time
static char sm_rgchLocalTimeCur[16]; // string version of time for logging
static char sm_rgchLocalDateCur[16]; // string version of date for logging
static RTime32 sm_nTimeLastSystemTimeUpdate; // last time we updated above two logging strings
bool m_bGMT;
};
#endif // RTIME_H
@@ -0,0 +1,64 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: interface to the game coordinator for this application
//
//=============================================================================
#ifndef ISTEAMGAMECOORDINATOR
#define ISTEAMGAMECOORDINATOR
#ifdef _WIN32
#pragma once
#endif
#include "steam/steamtypes.h"
#include "steam/steamclientpublic.h"
#include "steam/isteamclient.h"
// list of possible return values from the ISteamGameCoordinator API
enum EGCResults
{
k_EGCResultOK = 0,
k_EGCResultNoMessage = 1, // There is no message in the queue
k_EGCResultBufferTooSmall = 2, // The buffer is too small for the requested message
k_EGCResultNotLoggedOn = 3, // The client is not logged onto Steam
k_EGCResultInvalidMessage = 4, // Something was wrong with the message being sent with SendMessage
};
//-----------------------------------------------------------------------------
// Purpose: Functions for sending and receiving messages from the Game Coordinator
// for this application
//-----------------------------------------------------------------------------
class ISteamGameCoordinator
{
public:
// sends a message to the Game Coordinator
virtual EGCResults SendMessage( uint32 unMsgType, const void *pubData, uint32 cubData ) = 0;
// returns true if there is a message waiting from the game coordinator
virtual bool IsMessageAvailable( uint32 *pcubMsgSize ) = 0;
// fills the provided buffer with the first message in the queue and returns k_EGCResultOK or
// returns k_EGCResultNoMessage if there is no message waiting. pcubMsgSize is filled with the message size.
// If the provided buffer is not large enough to fit the entire message, k_EGCResultBufferTooSmall is returned
// and the message remains at the head of the queue.
virtual EGCResults RetrieveMessage( uint32 *punMsgType, void *pubDest, uint32 cubDest, uint32 *pcubMsgSize ) = 0;
};
#define STEAMGAMECOORDINATOR_INTERFACE_VERSION "SteamGameCoordinator001"
// callback notification - A new message is available for reading from the message queue
struct GCMessageAvailable_t
{
enum { k_iCallback = k_iSteamGameCoordinatorCallbacks + 1 };
uint32 m_nMessageSize;
};
// callback notification - A message failed to make it to the GC. It may be down temporarily
struct GCMessageFailed_t
{
enum { k_iCallback = k_iSteamGameCoordinatorCallbacks + 2 };
};
#endif // ISTEAMGAMECOORDINATOR
+729
View File
@@ -0,0 +1,729 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#ifdef TF
#include "steamcommon.h"
#define INCLUDED_STEAM2_USERID_STRUCTS
#include "steam/steamclientpublic.h"
#endif
#include "stdafx.h"
#ifdef HL1
#include "steamcommon.h"
#include "steam/steamclientpublic.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#ifndef UINT64_MAX
#define UINT64_MAX ((uint64)-1)
#endif
static const char *DecimalToUint64( const char *pchStr, uint64 unLimit,
uint64 *punVal )
{
const char *pchStart = pchStr;
uint64 unVal = 0;
while ( *pchStr >= '0' && *pchStr <= '9' )
{
uint64 unNext = unVal * 10;
if ( unNext < unVal )
{
// 64-bit overflow.
return NULL;
}
unVal = unNext + (uint64)( *pchStr - '0' );
if ( unVal > unLimit )
{
// Limit overflow.
return NULL;
}
pchStr++;
}
if ( pchStr == pchStart )
{
// No number at all.
return NULL;
}
*punVal = unVal;
return pchStr;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
// Input : pchSteamID - text representation of a Steam ID
//-----------------------------------------------------------------------------
CSteamID::CSteamID( const char *pchSteamID, EUniverse eDefaultUniverse /* = k_EUniverseInvalid */ )
{
SetFromString( pchSteamID, eDefaultUniverse );
}
//-----------------------------------------------------------------------------
// Purpose: Initializes a steam ID from a string
// Input : pchSteamID - text representation of a Steam ID
//-----------------------------------------------------------------------------
void CSteamID::SetFromString( const char *pchSteamID, EUniverse eDefaultUniverse )
{
uint nAccountID = 0;
uint nInstance = 1;
EUniverse eUniverse = eDefaultUniverse;
EAccountType eAccountType = k_EAccountTypeIndividual;
#ifdef DBGFLAG_ASSERT
// TF Merge -- Assert is debug-only and we have unused variable warnings on :-/
const char *pchSteamIDString = pchSteamID;
#endif
CSteamID StrictID;
StrictID.SetFromStringStrict( pchSteamID, eDefaultUniverse );
if ( *pchSteamID == '[' )
pchSteamID++;
// BUGBUG Rich use the Q_ functions
if (*pchSteamID == 'A')
{
// This is test only
pchSteamID++; // skip the A
eAccountType = k_EAccountTypeAnonGameServer;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
if ( strchr( pchSteamID, '(' ) )
sscanf( strchr( pchSteamID, '(' ), "(%u)", &nInstance );
const char *pchColon = strchr( pchSteamID, ':' );
if ( pchColon && *pchColon != 0 && strchr( pchColon+1, ':' ))
{
sscanf( pchSteamID, "%u:%u:%u", (uint*)&eUniverse, &nAccountID, &nInstance );
}
else if ( pchColon )
{
sscanf( pchSteamID, "%u:%u", (uint*)&eUniverse, &nAccountID );
}
else
{
sscanf( pchSteamID, "%u", &nAccountID );
}
if ( nAccountID == 0 )
{
// i dont care what number you entered
CreateBlankAnonLogon(eUniverse);
}
else
{
InstancedSet( nAccountID, nInstance, eUniverse, eAccountType );
}
// Catch cases where we're allowing sloppy input that we
// might not want to allow.
AssertMsg1( this->operator==( StrictID ), "Steam ID does not pass strict parsing: '%s'", pchSteamIDString );
return;
}
else if (*pchSteamID == 'G')
{
pchSteamID++; // skip the G
eAccountType = k_EAccountTypeGameServer;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
else if (*pchSteamID == 'C')
{
pchSteamID++; // skip the C
eAccountType = k_EAccountTypeContentServer;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
else if (*pchSteamID == 'g')
{
pchSteamID++; // skip the g
eAccountType = k_EAccountTypeClan;
nInstance = 0;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
else if (*pchSteamID == 'c')
{
pchSteamID++; // skip the c
eAccountType = k_EAccountTypeChat;
nInstance = k_EChatInstanceFlagClan;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
else if (*pchSteamID == 'L')
{
pchSteamID++; // skip the c
eAccountType = k_EAccountTypeChat;
nInstance = k_EChatInstanceFlagLobby;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
else if (*pchSteamID == 'T')
{
pchSteamID++; // skip the T
eAccountType = k_EAccountTypeChat;
nInstance = 0; // Anon chat
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
else if (*pchSteamID == 'U')
{
pchSteamID++; // skip the U
eAccountType = k_EAccountTypeIndividual;
nInstance = 1;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
else if (*pchSteamID == 'i')
{
pchSteamID++; // skip the i
eAccountType = k_EAccountTypeInvalid;
nInstance = 1;
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
if ( strchr( pchSteamID, ':' ) )
{
if (*pchSteamID == '[')
pchSteamID++; // skip the optional [
sscanf( pchSteamID, "%u:%u", (uint*)&eUniverse, &nAccountID );
if ( eUniverse == k_EUniverseInvalid )
eUniverse = eDefaultUniverse;
}
else
{
uint64 unVal64 = 0;
sscanf( pchSteamID, "%llu", &unVal64 );
if ( unVal64 > UINT_MAX )
{
// Assume a full 64-bit Steam ID.
SetFromUint64( unVal64 );
// Catch cases where we're allowing sloppy input that we
// might not want to allow.
AssertMsg1( this->operator==( StrictID ), "Steam ID does not pass strict parsing: '%s'", pchSteamIDString );
return;
}
else
{
nAccountID = (uint)unVal64;
}
}
Assert( (eUniverse > k_EUniverseInvalid) && (eUniverse < k_EUniverseMax) );
InstancedSet( nAccountID, nInstance, eUniverse, eAccountType );
// Catch cases where we're allowing sloppy input that we
// might not want to allow.
AssertMsg1( this->operator==( StrictID ), "Steam ID does not pass strict parsing: '%s'", pchSteamIDString );
}
// SetFromString allows many partially-correct strings, constraining how
// we might be able to change things in the future.
// SetFromStringStrict requires the exact string forms that we support
// and is preferred when the caller knows it's safe to be strict.
// Returns whether the string parsed correctly. The ID may
// still be invalid even if the string parsed correctly.
// If the string didn't parse correctly the ID will always be invalid.
bool CSteamID::SetFromStringStrict( const char *pchSteamID, EUniverse eDefaultUniverse )
{
uint nAccountID = 0;
uint nInstance = 1;
uint unMaxVal = 2;
EUniverse eUniverse = eDefaultUniverse;
EAccountType eAccountType = k_EAccountTypeIndividual;
char chPrefix;
bool bBracket = false;
bool bValid = true;
uint64 unVal[3];
const char *pchEnd;
// Start invalid.
Clear();
if ( !pchSteamID )
{
return false;
}
if ( *pchSteamID == '[' )
{
pchSteamID++;
bBracket = true;
}
chPrefix = *pchSteamID;
switch( chPrefix )
{
case 'A':
// This is test only
eAccountType = k_EAccountTypeAnonGameServer;
unMaxVal = 3;
break;
case 'G':
eAccountType = k_EAccountTypeGameServer;
break;
case 'C':
eAccountType = k_EAccountTypeContentServer;
break;
case 'g':
eAccountType = k_EAccountTypeClan;
nInstance = 0;
break;
case 'c':
eAccountType = k_EAccountTypeChat;
nInstance = k_EChatInstanceFlagClan;
break;
case 'L':
eAccountType = k_EAccountTypeChat;
nInstance = k_EChatInstanceFlagLobby;
break;
case 'T':
eAccountType = k_EAccountTypeChat;
nInstance = 0; // Anon chat
break;
case 'U':
eAccountType = k_EAccountTypeIndividual;
nInstance = 1;
break;
case 'i':
eAccountType = k_EAccountTypeInvalid;
nInstance = 1;
break;
default:
// We're reserving other leading characters so
// this should only be the plain-digits case.
if (chPrefix < '0' || chPrefix > '9')
{
bValid = false;
}
chPrefix = 0;
break;
}
if ( chPrefix )
{
pchSteamID++; // skip the prefix
if (*pchSteamID == '-' || *pchSteamID == ':')
pchSteamID++; // skip the optional - or :
}
uint unIdx = 0;
for (;;)
{
pchEnd = DecimalToUint64( pchSteamID, UINT64_MAX, &unVal[unIdx] );
if ( !pchEnd )
{
bValid = false;
break;
}
unIdx++;
// For 'A' we can have a trailing instance, which must
// be the end of the string.
if ( *pchEnd == '(' &&
chPrefix == 'A' )
{
if ( unIdx > 2 )
{
// Two instance IDs provided.
bValid = false;
}
pchEnd = DecimalToUint64( pchEnd + 1, k_unSteamAccountInstanceMask, &unVal[2] );
if ( !pchEnd ||
*pchEnd != ')' )
{
bValid = false;
break;
}
else
{
nInstance = (uint)unVal[2];
pchEnd++;
if ( *pchEnd == ':' )
{
// Not expecting more values.
bValid = false;
break;
}
}
}
if ( *pchEnd != ':' )
{
if ( bBracket )
{
if ( *pchEnd != ']' ||
*(pchEnd + 1) != 0 )
{
bValid = false;
}
}
else if ( *pchEnd != 0 )
{
bValid = false;
}
break;
}
if ( unIdx >= unMaxVal )
{
bValid = false;
break;
}
pchSteamID = pchEnd + 1;
}
if ( unIdx > 2 )
{
if ( unVal[2] <= k_unSteamAccountInstanceMask )
{
nInstance = (uint)unVal[2];
}
else
{
bValid = false;
}
}
if ( unIdx > 1 )
{
if ( unVal[0] >= k_EUniverseInvalid &&
unVal[0] < k_EUniverseMax )
{
eUniverse = (EUniverse)unVal[0];
if ( eUniverse == k_EUniverseInvalid )
eUniverse = eDefaultUniverse;
}
else
{
bValid = false;
}
if ( unVal[1] <= k_unSteamAccountIDMask )
{
nAccountID = (uint)unVal[1];
}
else
{
bValid = false;
}
}
else if ( unIdx > 0 )
{
if ( unVal[0] <= k_unSteamAccountIDMask )
{
nAccountID = (uint)unVal[0];
}
else if ( !chPrefix )
{
if ( bValid )
{
SetFromUint64( unVal[0] );
}
return bValid;
}
else
{
bValid = false;
}
}
else
{
bValid = false;
}
if ( bValid )
{
if ( chPrefix == 'A' )
{
if ( nAccountID == 0 )
{
// i dont care what number you entered
CreateBlankAnonLogon(eUniverse);
return bValid;
}
}
InstancedSet( nAccountID, nInstance, eUniverse, eAccountType );
}
return bValid;
}
#if defined( INCLUDED_STEAM2_USERID_STRUCTS )
//-----------------------------------------------------------------------------
// Purpose: Initializes a steam ID from a Steam2 ID string
// Input: pchSteam2ID - Steam2 ID (as a string #:#:#) to convert
// eUniverse - universe this ID belongs to
// Output: true if successful, false otherwise
//-----------------------------------------------------------------------------
bool CSteamID::SetFromSteam2String( const char *pchSteam2ID, EUniverse eUniverse )
{
Assert( pchSteam2ID );
// Convert the Steam2 ID string to a Steam2 ID structure
TSteamGlobalUserID steam2ID;
steam2ID.m_SteamInstanceID = 0;
steam2ID.m_SteamLocalUserID.Split.High32bits = 0;
steam2ID.m_SteamLocalUserID.Split.Low32bits = 0;
const char *pchTSteam2ID = pchSteam2ID;
// Customer support is fond of entering steam IDs in the following form: STEAM_n:x:y
const char *pchOptionalLeadString = "STEAM_";
if ( V_strnicmp( pchSteam2ID, pchOptionalLeadString, V_strlen( pchOptionalLeadString ) ) == 0 )
pchTSteam2ID = pchSteam2ID + V_strlen( pchOptionalLeadString );
char cExtraCharCheck = 0;
int cFieldConverted = sscanf( pchTSteam2ID, "%hu:%u:%u%c", &steam2ID.m_SteamInstanceID,
&steam2ID.m_SteamLocalUserID.Split.High32bits, &steam2ID.m_SteamLocalUserID.Split.Low32bits, &cExtraCharCheck );
// Validate the conversion ... a special case is steam2 instance ID 1 which is reserved for special DoD handling
if ( cExtraCharCheck != 0 || cFieldConverted == EOF || cFieldConverted < 2 || ( cFieldConverted < 3 && steam2ID.m_SteamInstanceID != 1 ) )
return false;
// Now convert to steam ID from the Steam2 ID structure
SetFromSteam2( &steam2ID, eUniverse );
return true;
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Renders the steam ID to a buffer. NOTE: for convenience of calling
// code, this code returns a pointer to a static buffer and is NOT thread-safe.
// Output: buffer with rendered Steam ID
//-----------------------------------------------------------------------------
const char * CSteamID::Render() const
{
// longest length of returned string is k_cBufLen
// [A:%u:%u:%u]
// %u == 10 * 3 + 6 == 36, plus terminator == 37
const int k_cBufLen = 37;
const int k_cBufs = 4; // # of static bufs to use (so people can compose output with multiple calls to Render() )
static char rgchBuf[k_cBufs][k_cBufLen];
static int nBuf = 0;
char * pchBuf = rgchBuf[nBuf]; // get pointer to current static buf
nBuf ++; // use next buffer for next call to this method
nBuf %= k_cBufs;
if ( k_EAccountTypeAnonGameServer == m_steamid.m_comp.m_EAccountType )
{
V_snprintf( pchBuf, k_cBufLen, "[A:%u:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID, m_steamid.m_comp.m_unAccountInstance );
}
else if ( k_EAccountTypeGameServer == m_steamid.m_comp.m_EAccountType )
{
V_snprintf( pchBuf, k_cBufLen, "[G:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else if ( k_EAccountTypeMultiseat == m_steamid.m_comp.m_EAccountType )
{
V_snprintf( pchBuf, k_cBufLen, "[M:%u:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID, m_steamid.m_comp.m_unAccountInstance );
}
else if ( k_EAccountTypePending == m_steamid.m_comp.m_EAccountType )
{
V_snprintf( pchBuf, k_cBufLen, "[P:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else if ( k_EAccountTypeContentServer == m_steamid.m_comp.m_EAccountType )
{
V_snprintf( pchBuf, k_cBufLen, "[C:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else if ( k_EAccountTypeClan == m_steamid.m_comp.m_EAccountType )
{
// 'g' for "group"
V_snprintf( pchBuf, k_cBufLen, "[g:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else if ( k_EAccountTypeChat == m_steamid.m_comp.m_EAccountType )
{
if ( m_steamid.m_comp.m_unAccountInstance & k_EChatInstanceFlagClan )
{
V_snprintf( pchBuf, k_cBufLen, "[c:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else if ( m_steamid.m_comp.m_unAccountInstance & k_EChatInstanceFlagLobby )
{
V_snprintf( pchBuf, k_cBufLen, "[L:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else // Anon chat
{
V_snprintf( pchBuf, k_cBufLen, "[T:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
}
else if ( k_EAccountTypeInvalid == m_steamid.m_comp.m_EAccountType )
{
V_snprintf( pchBuf, k_cBufLen, "[I:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else if ( k_EAccountTypeIndividual == m_steamid.m_comp.m_EAccountType )
{
if ( m_steamid.m_comp.m_unAccountInstance != k_unSteamUserDesktopInstance )
V_snprintf( pchBuf, k_cBufLen, "[U:%u:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID, m_steamid.m_comp.m_unAccountInstance );
else
V_snprintf( pchBuf, k_cBufLen, "[U:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else if ( k_EAccountTypeAnonUser == m_steamid.m_comp.m_EAccountType )
{
V_snprintf( pchBuf, k_cBufLen, "[a:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
else
{
V_snprintf( pchBuf, k_cBufLen, "[i:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
}
return pchBuf;
}
//-----------------------------------------------------------------------------
// Purpose: Renders the passed-in steam ID to a buffer. NOTE: for convenience of calling
// code, this code returns a pointer to a static buffer and is NOT thread-safe.
// Input: 64-bit representation of Steam ID to render
// Output: buffer with rendered Steam ID
//-----------------------------------------------------------------------------
const char * CSteamID::Render( uint64 ulSteamID )
{
CSteamID steamID( ulSteamID );
return steamID.Render();
}
//-----------------------------------------------------------------------------
// Purpose: some steamIDs are for internal use only
// This is really debug code, but we run with asserts on in retail, so ...
//-----------------------------------------------------------------------------
bool CSteamID::BValidExternalSteamID() const
{
if ( m_steamid.m_comp.m_EAccountType == k_EAccountTypePending )
return false;
if ( m_steamid.m_comp.m_EAccountType != k_EAccountTypeAnonGameServer && m_steamid.m_comp.m_EAccountType != k_EAccountTypeContentServer && m_steamid.m_comp.m_EAccountType != k_EAccountTypeAnonUser )
{
if ( m_steamid.m_comp.m_unAccountID == 0 && m_steamid.m_comp.m_unAccountInstance == 0 )
return false;
}
return true;
}
#ifdef STEAM
//-----------------------------------------------------------------------------
// Purpose: Returns the matching chat steamID, with the default instance of 0
// Input: SteamID, either a Clan or a Chat type
// Output: SteamID with account type changed to chat, and the Clan flag set.
// If account type was not chat to start with, instance will be set to 0
//-----------------------------------------------------------------------------
CSteamID ChatIDFromSteamID( const CSteamID &steamID )
{
if ( steamID.GetEAccountType() == k_EAccountTypeChat )
return steamID;
return ChatIDFromClanID( steamID );
}
//-----------------------------------------------------------------------------
// Purpose: Returns the matching chat steamID, with the default instance of 0
// Input: SteamID, either a Clan type or a Chat type w/ the Clan flag set
// Output: SteamID with account type changed to clan.
// If account type was not clan to start with, instance will be set to 0
//-----------------------------------------------------------------------------
CSteamID ClanIDFromSteamID( const CSteamID &steamID )
{
if ( steamID.GetEAccountType() == k_EAccountTypeClan )
return steamID;
return ClanIDFromChatID( steamID );
}
// Asserts steamID type before conversion
CSteamID ChatIDFromClanID( const CSteamID &steamIDClan )
{
Assert( steamIDClan.GetEAccountType() == k_EAccountTypeClan );
return CSteamID( steamIDClan.GetAccountID(), k_EChatInstanceFlagClan, steamIDClan.GetEUniverse(), k_EAccountTypeChat );
}
// Asserts steamID type before conversion
CSteamID ClanIDFromChatID( const CSteamID &steamIDChat )
{
Assert( steamIDChat.GetEAccountType() == k_EAccountTypeChat );
Assert( k_EChatInstanceFlagClan & steamIDChat.GetUnAccountInstance() );
return CSteamID( steamIDChat.GetAccountID(), 0, steamIDChat.GetEUniverse(), k_EAccountTypeClan );
}
//-----------------------------------------------------------------------------
// Purpose: CGameID "hidden" functions
// move these somewhere else maybe
//-----------------------------------------------------------------------------
CGameID::CGameID( const char *pchGameID )
{
m_ulGameID = 0;
sscanf( pchGameID, "%llu", &m_ulGameID );
switch ( m_gameID.m_nType )
{
default:
AssertMsg( false, "Unknown GameID type" );
m_ulGameID = 0;
break;
case k_EGameIDTypeApp:
case k_EGameIDTypeGameMod:
case k_EGameIDTypeShortcut:
case k_EGameIDTypeP2P:
break;
}
}
// renders this Game ID to string
const char * CGameID::Render() const
{
// longest buffer is log10(2**64) == 20 + 1 == 21
const int k_cBufLen = 21;
const int k_cBufs = 4; // # of static bufs to use (so people can compose output with multiple calls to Render() )
static char rgchBuf[k_cBufs][k_cBufLen];
static int nBuf = 0;
char * pchBuf = rgchBuf[nBuf]; // get pointer to current static buf
nBuf ++; // use next buffer for next call to this method
nBuf %= k_cBufs;
V_snprintf( pchBuf, k_cBufLen, "%llu", m_ulGameID );
return pchBuf;
}
// static method to render a uint64 representation of a Game ID to a string
const char * CGameID::Render( uint64 ulGameID )
{
CGameID nGameID( ulGameID );
return nGameID.Render();
}
#endif
+35
View File
@@ -0,0 +1,35 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: declares a variety of constants
//
// $NoKeywords: $
//=============================================================================
#ifndef T0CONSTANTS_H
#define T0CONSTANTS_H
#ifdef _WIN32
#pragma once
#endif
//-----------------------------------------------------------------------------
// numeric constants to avoid typos with wrong number of zeros
//-----------------------------------------------------------------------------
const int64 k_nMillion = 1000000;
const int64 k_nThousand = 1000;
const int64 k_nKiloByte = 1024;
const int64 k_nMegabyte = k_nKiloByte * k_nKiloByte;
//-----------------------------------------------------------------------------
// Timing constants
//-----------------------------------------------------------------------------
const unsigned int k_nSecondsPerHour = 60*60;
const unsigned int k_nSecondsPerDay = k_nSecondsPerHour * 24;
const int k_cSecondsPerMinute = 60;
const int k_cSecondsPerHour = k_cSecondsPerMinute * 60;
const int k_cSecondsPerDay = k_cSecondsPerHour * 24;
const int k_cSecondsPerWeek = k_cSecondsPerDay * 7;
const int k_cSecondsPerYear = k_cSecondsPerDay * 365;
#endif // T0CONSTANTS_H
+35
View File
@@ -0,0 +1,35 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================
#include "stdafx.h"
#include "pearsonshash.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
// This is a table of the values 0-255 in pseudo random order for use in our pearsons hash
// variant implemented below
const unsigned char g_CTHashRandomValues[256] =
{
131, 184, 146, 42, 124, 142, 226, 76, 8, 135, 215, 116, 228, 49, 144, 231,
238, 25, 156, 125, 128, 87, 223, 38, 98, 122, 105, 4, 35, 180, 188, 160,
179, 59, 218, 0, 192, 207, 209, 150, 227, 143, 140, 161, 73, 84, 111, 162,
239, 74, 210, 195, 15, 225, 104, 221, 245, 12, 72, 47, 109, 187, 40, 178,
208, 56, 190, 193, 126, 95, 33, 45, 177, 170, 186, 123, 202, 149, 60, 194,
168, 102, 71, 148, 46, 121, 52, 119, 196, 247, 127, 145, 75, 79, 61, 254,
9, 44, 23, 211, 18, 175, 251, 130, 203, 108, 85, 167, 29, 250, 138, 182,
101, 213, 159, 92, 36, 10, 86, 32, 176, 80, 17, 134, 181, 114, 64, 165,
89, 68, 6, 14, 205, 137, 117, 7, 39, 132, 26, 19, 214, 99, 166, 163,
69, 174, 157, 100, 201, 118, 2, 28, 235, 236, 139, 244, 70, 20, 155, 82,
51, 154, 115, 94, 93, 83, 136, 27, 198, 43, 50, 243, 183, 153, 53, 206,
77, 55, 57, 3, 220, 147, 253, 110, 37, 246, 97, 13, 120, 103, 91, 169,
58, 11, 133, 22, 152, 189, 222, 151, 141, 88, 224, 1, 48, 191, 249, 173,
106, 113, 252, 172, 232, 66, 219, 96, 237, 21, 233, 62, 242, 54, 230, 65,
78, 248, 16, 41, 31, 200, 90, 112, 255, 171, 164, 24, 199, 81, 212, 197,
185, 67, 5, 234, 30, 129, 216, 63, 204, 158, 217, 229, 107, 240, 241, 34,
};
+74
View File
@@ -0,0 +1,74 @@
//======= Copyright © Valve Corporation, All rights reserved. =================
//
// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose
// hash with a 32-bit output. References:
// http://code.google.com/p/smhasher/ (home of MurmurHash3)
// https://sites.google.com/site/murmurhash/avalanche
// http://www.strchr.com/hash_functions
//
//=============================================================================
#include <stdafx.h>
#include "murmurhash3.h"
//-----------------------------------------------------------------------------
uint32 MurmurHash3_32( const void * key, size_t len, uint32 seed, bool bCaselessStringVariant )
{
const uint8 * data = (const uint8*)key;
const ptrdiff_t nblocks = len / 4;
uint32 uSourceBitwiseAndMask = 0xDFDFDFDF | ((uint32)bCaselessStringVariant - 1);
uint32 h1 = seed;
//----------
// body
const uint32 * blocks = (const uint32 *)(data + nblocks*4);
for(ptrdiff_t i = -nblocks; i; i++)
{
uint32 k1 = LittleDWord(blocks[i]);
k1 &= uSourceBitwiseAndMask;
k1 *= 0xcc9e2d51;
k1 = (k1 << 15) | (k1 >> 17);
k1 *= 0x1b873593;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >> 19);
h1 = h1*5+0xe6546b64;
}
//----------
// tail
const uint8 * tail = (const uint8*)(data + nblocks*4);
uint32 k1 = 0;
switch(len & 3)
{
case 3: k1 ^= tail[2] << 16;
case 2: k1 ^= tail[1] << 8;
case 1: k1 ^= tail[0];
k1 &= uSourceBitwiseAndMask;
k1 *= 0xcc9e2d51;
k1 = (k1 << 15) | (k1 >> 17);
k1 *= 0x1b873593;
h1 ^= k1;
};
//----------
// finalization
h1 ^= len;
h1 ^= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ^= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ^= h1 >> 16;
return h1;
}
+100
View File
@@ -0,0 +1,100 @@
//======= Copyright © Valve Corporation, All rights reserved. =================
//
// Public domain MurmurHash3 by Austin Appleby is a very solid general-purpose
// hash with a 32-bit output. References:
// http://code.google.com/p/smhasher/ (home of MurmurHash3)
// https://sites.google.com/site/murmurhash/avalanche
// http://www.strchr.com/hash_functions
//
//=============================================================================
#ifndef MURMURHASH3_H
#define MURMURHASH3_H
#if defined(_WIN32)
#pragma once
#endif
uint32 MurmurHash3_32( const void *key, size_t len, uint32 seed, bool bCaselessStringVariant = false );
inline uint32 MurmurHash3String( const char *pszKey, size_t len )
{
return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, false );
}
inline uint32 MurmurHash3StringCaseless( const char *pszKey, size_t len )
{
return MurmurHash3_32( pszKey, len, 1047 /*anything will do for a seed*/, true );
}
inline uint32 MurmurHash3String( const char *pszKey )
{
return MurmurHash3String( pszKey, strlen( pszKey ) );
}
inline uint32 MurmurHash3StringCaseless( const char *pszKey )
{
return MurmurHash3StringCaseless( pszKey, strlen( pszKey ) );
}
template <typename T>
inline uint32 MurmurHash3Item( const T &item )
{
return MurmurHash3_32( &item, sizeof(item), 1047 );
}
inline uint32 MurmurHash3Int( uint32 h )
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
template <>
inline uint32 MurmurHash3Item( const uint32 &item )
{
return MurmurHash3Int( item );
}
template <>
inline uint32 MurmurHash3Item( const int32 &item )
{
return MurmurHash3Int( item );
}
template<typename T>
struct MurmurHash3Functor
{
typedef uint32 TargetType;
TargetType operator()(const T &key) const
{
return MurmurHash3Item( key );
}
};
template<>
struct MurmurHash3Functor<char *>
{
typedef uint32 TargetType;
TargetType operator()(const char *key) const
{
return MurmurHash3String( key );
}
};
template<>
struct MurmurHash3Functor<const char *>
{
typedef uint32 TargetType;
TargetType operator()(const char *key) const
{
return MurmurHash3String( key );
}
};
#endif // MURMURHASH3_H
+268
View File
@@ -0,0 +1,268 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: PearsonsHash.h
//
// This file contains implementation definitions of 'Pearson's' Hash'
// The generic implementation is a template, plus a couple of specializations
// for commonly used types.
//
// Take a look at http://en.wikipedia.org/wiki/Pearson_hashing for more info.
//
//=============================================================================
#ifndef _PEARSONSHASH_H_
#define _PEARSONSHASH_H_
#if defined(_WIN32) || defined(_WIN64)
#pragma once
#endif
extern const unsigned char g_CTHashRandomValues[256] ;
template<typename T>
struct PearsonsHashFunctor
{
typedef uint32 TargetType ;
TargetType operator()(const T& unKey) const
{
// This is a pearsons hash variant that returns a maximum of 32 bits
size_t size = sizeof(T);
const uint8 * k = (const uint8 *) &unKey;
uint32 byte_one = 0, byte_two = 0, byte_three = 0, byte_four = 0, n;
while (size)
{
--size;
n = *k++;
byte_one = g_CTHashRandomValues[byte_one ^ n];
if (size)
{
--size;
n = *k++;
byte_two = g_CTHashRandomValues[byte_two ^ n];
}
else
break;
if (size)
{
--size;
n = *k++;
byte_three = g_CTHashRandomValues[byte_three ^ n];
}
else
break;
if (size)
{
--size;
n = *k++;
byte_four = g_CTHashRandomValues[byte_four ^ n];
}
else
break;
}
TargetType idx = ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one;
return ( idx );
}
};
//
// We use this specialization for pointer types - it allows somebody
// to define a specialization for some complicated type, and then use a
// pointer to that type as a key, and have that automatically go to the
// specialization for the complicated type !
//
template<typename T>
struct PearsonsHashFunctor<T*>
{
typedef uint32 TargetType ;
TargetType operator()(const T* key) const
{
PearsonsHashFunctor<T> functor ;
return functor(*key) ;
}
};
//
// This functor specializes for unsigned 32 bit integers, a commonly used type in Steam.
// It should return the exact same result as the unspecialized version on Intel Architecture machines.
//
template<>
struct PearsonsHashFunctor<uint32>
{
typedef uint32 TargetType ;
TargetType operator()(const uint32 unKey) const
{
uint32 byte_one = g_CTHashRandomValues[(unKey>>0) & 0xff];
uint32 byte_two = g_CTHashRandomValues[(unKey>>8) & 0xff];
uint32 byte_three = g_CTHashRandomValues[(unKey>>16) & 0xff];
uint32 byte_four = g_CTHashRandomValues[(unKey>>24)&0xff];
return ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one;
}
};
//
// This functor specializes for unsigned 64 bit integers, another commonly used type in Steam.
// It should return the exact same result as the unspecialized version on Intel Architecture machines.
//
template<>
struct PearsonsHashFunctor<uint64>
{
typedef uint32 TargetType ;
TargetType operator()(const uint64 unKey) const
{
//
// Note that we pull apart the 64 bits in Intel's endian order.
//
uint32 n;
//
// On Intel Machines, to make this return the exact same result as the generic version
// we have to go from least significant byte to most significant byte !
//
n = static_cast<uint32>((unKey >> (0)) & 0xff) ;
uint32 byte_one = g_CTHashRandomValues[n];
n = static_cast<uint32>((unKey >> (8)) & 0xff) ;
uint32 byte_two = g_CTHashRandomValues[n];
n = static_cast<uint32>((unKey >> (16)) & 0xff) ;
uint32 byte_three = g_CTHashRandomValues[n];
n = static_cast<uint32>((unKey >> (24)) & 0xff) ;
uint32 byte_four = g_CTHashRandomValues[n];
n = static_cast<uint32>((unKey >> (32)) & 0xff) ;
byte_one = g_CTHashRandomValues[n ^ byte_one];
n = static_cast<uint32>((unKey >> (8+32)) & 0xff) ;
byte_two = g_CTHashRandomValues[n ^ byte_two];
n = static_cast<uint32>((unKey >> (16+32)) & 0xff) ;
byte_three = g_CTHashRandomValues[n ^ byte_three];
n = static_cast<uint32>((unKey >> (24+32)) & 0xff) ;
byte_four = g_CTHashRandomValues[n ^ byte_four];
return ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one;
}
};
//
// This functor specializes for C standard NULL terminated strings !
// It is setup so that if you had a char array containing a NULL terminated string
// and correctly sized, ie char rgch[16] = { "123456789012345" } and a
// null terminated string i.e. char *sz = "123456789012345" these will return identical
// results, and both include the NULL terminator in the hash calculation.
//
template<>
struct PearsonsHashFunctor<char*>
{
typedef uint32 TargetType ;
TargetType operator()(const char* szKey) const
{
const uint8 * k = (const uint8 *) szKey ;
uint32 byte_one = 0, byte_two = 0, byte_three = 0, byte_four = 0, n;
do
{
n = *k++;
byte_one = g_CTHashRandomValues[byte_one ^ n];
if (n=='\0')
break;
n = *k++;
byte_two = g_CTHashRandomValues[byte_two ^ n];
if (n=='\0')
break;
n = *k++;
byte_three = g_CTHashRandomValues[byte_three ^ n];
if (n=='\0')
break;
n = *k++;
byte_four = g_CTHashRandomValues[byte_four ^ n];
} while(n!='\0') ;
TargetType idx = ( byte_four << 24 ) | ( byte_three << 16 ) | ( byte_two << 8 ) | byte_one;
return ( idx );
}
};
//
// This functor compares two objects of a particular type and returns a result
// that follows the strcmp/memcmp.
// If the type doesn't provide an operator== or operator< then you can provide
// a type specific specialization to override this defualt functor !
//
template<typename T>
struct ComparisonFunctor
{
int operator()(const T &lhs, const T &rhs) const
{
if( lhs == rhs )
return 0 ;
else if( lhs < rhs )
return -1 ;
else
return 1 ;
}
};
//
// I expect people to build Comparison specializations for full types,
// not pointer types - this Specialization should allow the C++ compiler
// to bind to a specialization for a particular type.
// We do not want to compare pointers for equality, and a memcmp() may
// not be good for a complicated type !
//
template<typename T>
struct ComparisonFunctor<T*>
{
int operator()(const T *lhs, const T *rhs) const
{
ComparisonFunctor<T> functor ;
return functor(*lhs, *rhs) ;
}
};
template<>
struct ComparisonFunctor<int>
{
int operator()(const int lhs, const int rhs) const
{
return lhs-rhs ;
}
};
template<>
struct ComparisonFunctor<unsigned int>
{
int operator()(const unsigned int lhs, const unsigned int rhs) const
{
return static_cast<int>(lhs) - static_cast<int>(rhs) ;
}
};
template<>
struct ComparisonFunctor<uint64>
{
int operator()(const uint64 lhs, const uint64 rhs) const
{
if( lhs < rhs )
return -1 ;
else if( lhs == rhs )
return 0 ;
else
return 1 ;
}
};
template<>
struct ComparisonFunctor<char*>
{
int operator()(const char * lhs, const char* rhs) const
{
return Q_strcmp(lhs, rhs) ;
}
};
#endif // _PEARSONSHASH_H_
+255
View File
@@ -0,0 +1,255 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//=============================================================================
//#include "pch_vstdlib.h"
#include "stdafx.h"
#include "tier0/tslist.h"
#include "tier0/t0constants.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const uint k_cubBytesAllocatedToConsiderFreeingMemory = 5 * k_nMegabyte;
static const int k_cBlocksAllocatedToConsiderFreeingMemory = 10;
typedef TSLNodeBase_t FreeListItem_t;
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CThreadSafeMemoryPool::CThreadSafeMemoryPool( int blockSize, int numElements, int growMode )
{
m_ptslistFreeBlocks = new CTSListBase;
// round up to the nearest 8-byte boundary
if ( blockSize % TSLIST_NODE_ALIGNMENT != 0 )
{
blockSize += TSLIST_NODE_ALIGNMENT - (blockSize % TSLIST_NODE_ALIGNMENT);
}
Assert( blockSize % TSLIST_NODE_ALIGNMENT == 0 );
Assert( blockSize > sizeof(FreeListItem_t) );
m_nGrowMode = growMode;
m_cubBlockSize = blockSize;
m_nGrowSize = numElements;
m_cubAllocated = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Frees the memory contained in the mempool
//-----------------------------------------------------------------------------
CThreadSafeMemoryPool::~CThreadSafeMemoryPool()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
FOR_EACH_VEC( m_vecBlockSets, i )
{
_aligned_free( m_vecBlockSets[i].m_pubBlockSet );
}
delete m_ptslistFreeBlocks;
}
//-----------------------------------------------------------------------------
// Purpose: Frees everything
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Clear()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
ClearNoLock();
}
//-----------------------------------------------------------------------------
// Purpose: Frees everything
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::ClearNoLock()
{
FOR_EACH_VEC( m_vecBlockSets, i )
{
_aligned_free( m_vecBlockSets[i].m_pubBlockSet );
}
m_ptslistFreeBlocks->Detach();
m_cubAllocated = 0;
m_cBlocksInUse = 0;
m_vecBlockSets.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a single block of memory from the pool.
//-----------------------------------------------------------------------------
void *CThreadSafeMemoryPool::Alloc()
{
return Alloc( m_cubBlockSize );
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a single block of memory from the pool.
//-----------------------------------------------------------------------------
void *CThreadSafeMemoryPool::Alloc( unsigned int amount )
{
// loop attempting to get memory
// there appears to be a case where memory corruption can get this into an infinite loop
// normally 1 or 2 attempts are necessary to get a block, so if we hit 1000 we know something is wrong
int cAttempts = 1000;
while ( --cAttempts )
{
// pull first from the free list
m_threadRWLock.LockForRead();
FreeListItem_t *pFreeListItem = m_ptslistFreeBlocks->Pop();
if ( pFreeListItem )
{
m_threadRWLock.UnlockRead();
m_cBlocksInUse++;
return (void *)pFreeListItem;
}
m_threadRWLock.UnlockRead();
// no free items, add a new block
// switch from a read lock to a write lock
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
// another thread may have allocated memory; try the free list again if so
if ( m_ptslistFreeBlocks->Count() > 0 )
continue;
size_t cubBlob = m_nGrowSize * m_cubBlockSize;
if ( m_nGrowMode == GROW_FAST )
{
cubBlob *= (m_vecBlockSets.Count() + 1);
}
// don't grow if we're told not to
if ( m_nGrowMode == GROW_NONE && m_vecBlockSets.Count() == 1 )
return NULL;
// allocate, but we can fail
byte *pBlobBase = (byte *)MemAlloc_AllocAligned( cubBlob, TSLIST_NODE_ALIGNMENT /*, (m_nGrowMode == GROW_TIL_YOU_CANT)*/ );
if ( !pBlobBase )
return NULL;
byte *pBlobEnd = pBlobBase + cubBlob;
// add all the items to the pool
for ( byte *pBlob = pBlobBase; pBlob < pBlobEnd; pBlob += m_cubBlockSize )
{
m_ptslistFreeBlocks->Push( (FreeListItem_t *)pBlob );
}
m_cubAllocated += cubBlob;
BlockSet_t blockSet = { pBlobBase, cubBlob };
m_vecBlockSets.AddToTail( blockSet );
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose: Frees a block of memory
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Free( void *pMem )
{
Free( pMem, m_cubBlockSize );
}
//-----------------------------------------------------------------------------
// Purpose: Frees a block of memory
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Free( void *pMem, int cubAlloc )
{
m_threadRWLock.LockForRead();
// push the item back onto the free list
m_ptslistFreeBlocks->Push( (FreeListItem_t *)pMem );
m_cBlocksInUse--;
m_threadRWLock.UnlockRead();
// if we're completely free, and have too much memory allocated, free some
if ( m_cBlocksInUse == 0
&& m_cubAllocated >= k_cubBytesAllocatedToConsiderFreeingMemory
&& m_vecBlockSets.Count() >= k_cBlocksAllocatedToConsiderFreeingMemory )
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
// check again nothing is in use
if ( m_cBlocksInUse == 0 )
{
// free all the blocks
ClearNoLock();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: display
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::PrintStats()
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
int cBlocksInUse = m_cBlocksInUse;
Msg( "Block size: %-11s Alloc'd: %8d Num blobs: %5d (%s)\n", Q_pretifymem( m_cubBlockSize, 2, true ),
cBlocksInUse, m_vecBlockSets.Count(), Q_pretifymem( m_cubAllocated, 2, true ) );
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
size_t CThreadSafeMemoryPool::CubTotalSize()
{
return m_cubAllocated;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
size_t CThreadSafeMemoryPool::CubSizeInUse()
{
return m_cBlocksInUse * m_cubBlockSize;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
int CThreadSafeMemoryPool::Count()
{
return m_cBlocksInUse;
}
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Run a global validation pass on all of our data structures and memory
// allocations.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CThreadSafeMemoryPool::Validate( CValidator &validator, const char *pchName )
{
AUTO_LOCK_SPIN_WRITE( m_threadRWLock );
VALIDATE_SCOPE();
FOR_EACH_VEC( m_vecBlockSets, i )
{
validator.ClaimMemory( MemAlloc_Unalign( m_vecBlockSets[i].m_pubBlockSet ) );
}
ValidateObj( m_vecBlockSets );
validator.ClaimMemory( MemAlloc_Unalign( m_ptslistFreeBlocks ) );
}
#endif // DBGFLAG_VALIDATE
+170
View File
@@ -0,0 +1,170 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#ifndef TSMEMPOOL_H
#define TSMEMPOOL_H
#ifdef _WIN32
#pragma once
#endif
#undef new
//-----------------------------------------------------------------------------
// Purpose: Optimized pool memory allocator
//-----------------------------------------------------------------------------
class CThreadSafeMemoryPool
{
public:
enum
{
GROW_NONE=0, // Don't allow new blobs.
GROW_FAST=1, // New blob size is numElements * (i+1) (ie: the blocks it allocates get larger and larger each time it allocates one).
GROW_SLOW=2, // New blob size is numElements.
GROW_TIL_YOU_CANT=3 // GROW_SLOW til alloc fails - then STOP and dont assert!
};
CThreadSafeMemoryPool( int blockSize, int numElements, int growMode = GROW_FAST );
~CThreadSafeMemoryPool();
void *Alloc(); // Allocate the element size you specified in the constructor.
void *Alloc( unsigned int cubAlloc );
void Free( void *pMem );
void Free( void *pMem, int cubAlloc );
// Frees everything
void Clear();
// display
void PrintStats();
size_t CubTotalSize();
size_t CubSizeInUse();
int Count();
static void * operator new( size_t size )
{
CThreadSafeMemoryPool *pNode = (CThreadSafeMemoryPool *)MemAlloc_AllocAligned( size, 8, __FILE__, __LINE__
#ifdef STEAM
, true // new operator
#endif
);
return pNode;
}
static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine )
{
CThreadSafeMemoryPool *pNode = (CThreadSafeMemoryPool *)MemAlloc_AllocAligned( size, 8, pFileName, nLine
#ifdef STEAM
, true // new operator
#endif
);
return pNode;
}
static void operator delete( void *p)
{
MemAlloc_FreeAligned( p
#ifdef STEAM
, true // new operator
#endif
);
}
static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine )
{
MemAlloc_FreeAligned( p
#ifdef STEAM
, true // new operator
#endif
);
}
#ifdef DBGFLAG_VALIDATE
void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
#endif // DBGFLAG_VALIDATE
private:
// These ain't gonna work
static void * operator new[] ( size_t size );
static void operator delete [] ( void *p);
// CThreadSpinRWLock needs 8 byte alignment to work but we new() CThreadSafeMemoryPool
// so simply place it at the start of the class to make sure it fits on the 8-byte boundary
CThreadSpinRWLock m_threadRWLock;
int m_nGrowMode;
int m_cubBlockSize;
int m_nGrowSize;
void ClearNoLock();
CInterlockedInt m_cBlocksInUse;
size_t m_cubAllocated;
struct BlockSet_t
{
byte *m_pubBlockSet;
size_t m_cubAllocated;
};
CUtlVector<BlockSet_t> m_vecBlockSets;
class CTSListBase *m_ptslistFreeBlocks;
};
//-----------------------------------------------------------------------------
// Wrapper macro to make an allocator that returns particular typed allocations
// and construction and destruction of objects.
//-----------------------------------------------------------------------------
template< class T >
class CThreadSafeClassMemoryPool : public CThreadSafeMemoryPool
{
public:
CThreadSafeClassMemoryPool(int numElements, int growMode = GROW_FAST) :
CThreadSafeMemoryPool( sizeof(T), numElements, growMode ) {}
T* Alloc();
void Free( T *pMem );
};
template< class T >
T* CThreadSafeClassMemoryPool<T>::Alloc()
{
T *pRet = (T*)CThreadSafeMemoryPool::Alloc();
if ( pRet )
{
Construct( pRet );
}
return pRet;
}
template< class T >
void CThreadSafeClassMemoryPool<T>::Free(T *pMem)
{
if ( pMem )
{
Destruct( pMem );
}
CThreadSafeMemoryPool::Free( pMem );
}
#endif // TSMEMPOOL_H
+383
View File
@@ -0,0 +1,383 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include <stdafx.h>
#include "tier0/t0constants.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static const int k_cubMemBlockPrefixSize = sizeof(uint32);
#define ALLOCSIZE_TO_LOOKUP( cubAlloc ) ( (cubAlloc - 1) >> 5 )
#define LOOKUP_TO_ALLOCSIZE( iLookup ) ( (iLookup << 5) + 1 )
//-----------------------------------------------------------------------------
// Purpose: constructor, the sizes in pMemPoolConfig must be in ascending order
//-----------------------------------------------------------------------------
CThreadSafeMultiMemoryPool::CThreadSafeMultiMemoryPool( const MemPoolConfig_t *pMemPoolConfig, int cnMemPoolConfig, int nGrowMode /*= GROW_FAST*/ )
{
m_cubReallocedTotal = 0;
m_MapRawAllocation.SetLessFunc( DefLessFunc( void * ) );
for ( int iMemPoolConfig = 0; iMemPoolConfig < cnMemPoolConfig; iMemPoolConfig++ )
{
MemPoolRecord_t memPoolRecord;
// verify that the mem pool sizes are in ascending order
Assert( iMemPoolConfig == 0 || ( iMemPoolConfig > 0 && pMemPoolConfig[ iMemPoolConfig - 1 ].m_cubBlockSize < pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize ) );
AssertMsg( pMemPoolConfig[ iMemPoolConfig].m_cubBlockSize % 32 == 0, "Mempools sizes must be multiples of 32" );
// add an int to the block size so we can note the alloc size
memPoolRecord.m_pMemPool = new CThreadSafeMemoryPool( pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize + k_cubMemBlockPrefixSize,
pMemPoolConfig[ iMemPoolConfig ].m_cubDefaultPoolSize, nGrowMode );
Assert( memPoolRecord.m_pMemPool );
memPoolRecord.m_nBlockSize = pMemPoolConfig[ iMemPoolConfig ].m_cubBlockSize;
m_VecMemPool.AddToTail( memPoolRecord );
// update the largest blocksize
m_nBlockSizeMax = MAX( m_nBlockSizeMax, memPoolRecord.m_nBlockSize );
}
// build the lookup table
int nLookupMax = m_nBlockSizeMax >> 5;
m_VecMemPoolLookup.AddMultipleToTail( nLookupMax );
for ( int i = 0; i < nLookupMax; i++ )
{
uint32 cubAllocSize = LOOKUP_TO_ALLOCSIZE( i );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
if ( m_VecMemPool[iMemPool].m_nBlockSize >= cubAllocSize )
{
m_VecMemPoolLookup[i] = &m_VecMemPool[iMemPool];
break;
}
}
}
#if defined(_DEBUG)
// validate the lookup table
for ( int i = 1; i < (int)m_nBlockSizeMax; i++ )
{
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
if ( (int)m_VecMemPool[iMemPool].m_nBlockSize >= i )
{
AssertMsg( m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP(i)] == &m_VecMemPool[iMemPool], "Invalid mempool block size, can't generate lookup table" );
break;
}
}
}
#endif // _DEBUG
}
//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CThreadSafeMultiMemoryPool::~CThreadSafeMultiMemoryPool()
{
AUTO_LOCK( m_mutexRawAllocations );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool ++ )
{
delete m_VecMemPool[iMemPool].m_pMemPool;
}
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
}
}
//-----------------------------------------------------------------------------
// Purpose: Allocates a block of memory at of least nAllocSize bytes
// Input : nAllocSize - number of bytes to alloc
// Output : pointer to memory alloc'd, NULL on error
//-----------------------------------------------------------------------------
void *CThreadSafeMultiMemoryPool::Alloc( uint32 cubAllocSize )
{
if ( cubAllocSize == 0 )
return NULL;
if ( cubAllocSize <= m_nBlockSizeMax )
{
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
void *pvMem = pMemPoolRecord->m_pMemPool->Alloc( cubAllocSize + k_cubMemBlockPrefixSize );
*(uint32 *)pvMem = cubAllocSize;
return ( (char *)pvMem + k_cubMemBlockPrefixSize );
}
// can't fit in our mem pools, alloc it in our one off buffer
RawAllocation_t rawAllocation;
rawAllocation.m_nBlockSize = cubAllocSize;
rawAllocation.m_pvMem = PvAlloc( cubAllocSize + k_cubMemBlockPrefixSize );
if ( !rawAllocation.m_pvMem )
{
return NULL;
}
*(uint32 *)rawAllocation.m_pvMem = rawAllocation.m_nBlockSize;
AUTO_LOCK( m_mutexRawAllocations );
m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
return ( (char *)rawAllocation.m_pvMem + k_cubMemBlockPrefixSize );
}
//-----------------------------------------------------------------------------
// Purpose: Free a previously alloc'd block
// Input : pMem - memory to free
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::Free( void *pvMem )
{
if ( !pvMem )
return;
uint32 cubAllocSize = *( (uint32 *)pvMem - 1 );
if ( cubAllocSize <= m_nBlockSizeMax )
{
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubAllocSize )];
pMemPoolRecord->m_pMemPool->Free( (char *)pvMem - k_cubMemBlockPrefixSize, cubAllocSize + k_cubMemBlockPrefixSize );
return;
}
AUTO_LOCK( m_mutexRawAllocations );
// must have been alloc'd from the raw heap, find it in map
void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
{
AssertMsg3( false, "CThreadSafeMultiMemoryPool::Free: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
pvMem, pvAllocedMem, cubAllocSize );
return;
}
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
m_MapRawAllocation.RemoveAt( iRawAllocation);
}
//-----------------------------------------------------------------------------
// Purpose: Return the size alloc'd for this block
// Input : pMem - memory to report
// Output : size in bytes of this memory
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CubAllocSize(void *pvMem)
{
if ( !pvMem )
{
return -1;
}
return *(((uint32 *)pvMem) -1);
}
//-----------------------------------------------------------------------------
// Purpose: Frees all previously alloc'd memory
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::Clear()
{
AUTO_LOCK( m_mutexRawAllocations );
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
m_VecMemPool[iMemPool].m_pMemPool->Clear();
}
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
FreePv( m_MapRawAllocation[iRawAllocation].m_pvMem );
}
m_MapRawAllocation.RemoveAll();
}
//-----------------------------------------------------------------------------
// Purpose: print to the console info about our storage
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::PrintStats()
{
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
m_VecMemPool[iMemPool].m_pMemPool->PrintStats();
}
int cubRawBytesAllocd = 0;
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
Msg( "Raw bytes alloc'd: %s\n", Q_pretifymem( cubRawBytesAllocd, 2, true ) );
Msg( "Cumulative bytes re-alloced: %s\n", Q_pretifymem( m_cubReallocedTotal, 2, true ) );
}
//-----------------------------------------------------------------------------
// Purpose: return the total mem alloced by this pool in MB
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CMBPoolSize() const
{
uint64 cubRawBytesAllocd = 0;
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubTotalSize() );
}
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
return ( cubRawBytesAllocd / k_nMegabyte );
}
//-----------------------------------------------------------------------------
// Purpose: return the total mem alloced by this pool in MB
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::CMBPoolSizeInUse() const
{
uint64 cubRawBytesAllocd = 0;
for ( int iMemPool= 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cubRawBytesAllocd += ( m_VecMemPool[iMemPool].m_pMemPool->CubSizeInUse() );
}
AUTO_LOCK( m_mutexRawAllocations );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
cubRawBytesAllocd += m_MapRawAllocation[iRawAllocation].m_nBlockSize;
}
return ( cubRawBytesAllocd / k_nMegabyte );
}
//-----------------------------------------------------------------------------
// Purpose: return number of mempool blocks alloc'd
//-----------------------------------------------------------------------------
int CThreadSafeMultiMemoryPool::Count()
{
int cCount = 0;
for ( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
cCount += m_VecMemPool[iMemPool].m_pMemPool->Count();
}
return cCount;
}
//-----------------------------------------------------------------------------
// Purpose: reallocate an existing block of memory to a new size (and copy the data
// Input: pvMem - a pointer to the existing memory
// cubAlloc - number of bytes to alloc
// Output: returns a pointer to the memory allocated (NULL on error)
//-----------------------------------------------------------------------------
void *CThreadSafeMultiMemoryPool::ReAlloc( void *pvMem, uint32 cubAlloc )
{
uint32 cubOldAlloc = CubAllocSize(pvMem);
if ( pvMem && cubAlloc <= cubOldAlloc )
return pvMem;
if ( cubOldAlloc > m_nBlockSizeMax )
{
AUTO_LOCK( m_mutexRawAllocations );
// okay, must have been alloc'd from the raw heap, search for it
void *pvAllocedMem = (char *)pvMem - k_cubMemBlockPrefixSize;
int iRawAllocation = m_MapRawAllocation.Find( pvAllocedMem );
if ( m_MapRawAllocation.InvalidIndex() == iRawAllocation )
{
AssertMsg3( false, "CThreadSafeMultiMemoryPool::ReAlloc: raw allocation %p (original alloc: %p, %d bytes) not found in allocation map",
pvMem, pvAllocedMem, cubOldAlloc );
return NULL;
}
// realloc the memory
void *pvNewMem = PvRealloc( pvAllocedMem, cubAlloc + k_cubMemBlockPrefixSize );
if ( !pvNewMem )
{
m_MapRawAllocation.RemoveAt( iRawAllocation );
return NULL;
}
// update our tracking
*(uint32 *)pvNewMem = cubAlloc;
if ( pvAllocedMem == pvNewMem )
{
// if pointer is the same, use the same map entry with the same key (the pointer given to caller)
m_MapRawAllocation[iRawAllocation].m_pvMem = pvNewMem;
m_MapRawAllocation[iRawAllocation].m_nBlockSize = cubAlloc;
}
else
{
// if pointer changed, need to remove the old entry and re-insert with new key
m_MapRawAllocation.RemoveAt( iRawAllocation );
RawAllocation_t rawAllocation;
rawAllocation.m_pvMem = pvNewMem;
rawAllocation.m_nBlockSize = cubAlloc;
m_MapRawAllocation.Insert( rawAllocation.m_pvMem, rawAllocation );
}
return ( (char *)pvNewMem + k_cubMemBlockPrefixSize );
}
else
{
// see if we can stay in the same block
MemPoolRecord_t *pMemPoolRecord = m_VecMemPoolLookup[ALLOCSIZE_TO_LOOKUP( cubOldAlloc )];
if ( cubAlloc <= pMemPoolRecord->m_nBlockSize )
{
// re-assign the size
*((uint32 *)pvMem - 1) = cubAlloc;
return pvMem;
}
void *pvNewMem = Alloc( cubAlloc );
if ( !pvNewMem )
{
return NULL;
}
m_cubReallocedTotal += cubOldAlloc;
Q_memcpy( pvNewMem, pvMem, cubOldAlloc );
Free( pvMem ); // now free the old memory buffer we had
return pvNewMem;
}
}
#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Ensure that all of our internal structures are consistent, and
// account for all memory that we've allocated.
// Input: validator - Our global validator object
// pchName - Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void CThreadSafeMultiMemoryPool::Validate( CValidator &validator, const char *pchName )
{
validator.Push( "CThreadSafeMultiMemoryPool", this, pchName );
ValidateObj( m_VecMemPool );
for( int iMemPool = 0; iMemPool < m_VecMemPool.Count(); iMemPool++ )
{
validator.ClaimMemory_Aligned( m_VecMemPool[iMemPool].m_pMemPool );
m_VecMemPool[iMemPool].m_pMemPool->Validate( validator, "m_VecMemPool[iMemPool].m_pMemPool" );
}
AUTO_LOCK( m_mutexRawAllocations );
ValidateObj( m_MapRawAllocation );
FOR_EACH_MAP_FAST( m_MapRawAllocation, iRawAllocation )
{
validator.ClaimMemory( m_MapRawAllocation[iRawAllocation].m_pvMem );
}
ValidateObj( m_VecMemPoolLookup );
validator.Pop();
}
#endif // DBGFLAG_VALIDATE
+97
View File
@@ -0,0 +1,97 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose:
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#ifndef TSMULTIMEMPOOL_H
#define TSMULTIMEMPOOL_H
#ifdef _WIN32
#pragma once
#endif
#include "tier1/utlmap.h"
#include "tier1/mempool.h"
#include "tier1/tsmempool.h"
//-----------------------------------------------------------------------------
// Purpose: A container of a range of mem pool sizes (for network buffers for example)
// and a raw alloc capability (for sizes greater than any contained mem pool).
//-----------------------------------------------------------------------------
class CThreadSafeMultiMemoryPool
{
public:
struct MemPoolConfig_t
{
uint32 m_cubBlockSize;
uint32 m_cubDefaultPoolSize;
};
CThreadSafeMultiMemoryPool( const MemPoolConfig_t *pnBlock, int cnMemPoolConfig, int nGrowMode = UTLMEMORYPOOL_GROW_FAST );
~CThreadSafeMultiMemoryPool();
// Allocate a block of at least nAllocSize bytes
void* Alloc( uint32 cubAlloc );
// Free a previously alloc'd block
void Free(void *pvMem);
// ReAllocate a previously allocated block to a new size
void* ReAlloc( void *pvMem, uint32 cubAlloc );
// Frees everything
void Clear();
// alloc size for this bit of alloc'd memory
int CubAllocSize( void *pvMem );
// prints details about our contained memory
void PrintStats();
// total number of alloc'd elements
int Count();
// Return the total size in MB allocated for this pool
int CMBPoolSize() const;
// Return the amount of memory in use
int CMBPoolSizeInUse() const;
#ifdef DBGFLAG_VALIDATE
void Validate( CValidator &validator, const char *pchName ); // Validate our internal structures
#endif // DBGFLAG_VALIDATE
private:
struct MemPoolRecord_t
{
CThreadSafeMemoryPool *m_pMemPool;
uint32 m_nBlockSize;
};
CUtlVector<MemPoolRecord_t> m_VecMemPool; // stores our list of mem pools
uint32 m_nBlockSizeMax;
CUtlVector<MemPoolRecord_t *> m_VecMemPoolLookup; // quick lookup table of mempools
struct RawAllocation_t
{
void *m_pvMem;
uint32 m_nBlockSize;
};
CUtlMap<void *,RawAllocation_t,int> m_MapRawAllocation; // stores our list of raw alloc'd mem
CThreadFastMutex m_mutexRawAllocations;
uint32 m_cubReallocedTotal;
};
#endif // TSMULTIMEMPOOL_H
+693
View File
@@ -0,0 +1,693 @@
//========= Copyright Valve Corporation, All rights reserved. =================//
//
// Purpose: index-based hash map container well suited for large and growing
// datasets. It uses less memory than other hash maps and incrementally
// rehashes to reduce reallocation spikes.
//
//=============================================================================//
#ifndef UTLHASHMAPLARGE_H
#define UTLHASHMAPLARGE_H
#ifdef _WIN32
#pragma once
#endif
#include "tier0/dbg.h"
#include "bitvec.h"
#include "tier1/murmurhash3.h"
// fast mod for power of 2 numbers
namespace basetypes
{
template <class T>
inline bool IsPowerOf2(T n)
{
return n > 0 && (n & (n-1)) == 0;
}
template <class T1, class T2>
inline T2 ModPowerOf2(T1 a, T2 b)
{
return T2(a) & (b-1);
}
}
// default comparison operator
template <typename T>
class CDefEquals
{
public:
CDefEquals() {}
CDefEquals( int i ) {}
inline bool operator()( const T &lhs, const T &rhs ) const { return ( lhs == rhs ); }
inline bool operator!() const { return false; }
};
// Specialization to compare pointers
template <typename T>
class CDefEquals<T*>
{
public:
CDefEquals() {}
CDefEquals( int i ) {}
inline bool operator()( const T *lhs, const T *rhs ) const
{
if ( lhs == rhs )
return true;
else if ( NULL == lhs || NULL == rhs )
return false;
else
return ( *lhs == *rhs );
}
inline bool operator!() const { return false; }
};
// Hash specialization for CUtlStrings
template<>
struct MurmurHash3Functor<CUtlString>
{
typedef uint32 TargetType ;
TargetType operator()(const CUtlString &strKey) const
{
return MurmurHash3Functor<const char*>()( strKey.String() );
}
};
//hash 3 function for a general case sensitive string compares
struct MurmurHash3ConstCharPtr
{
typedef uint32 TargetType ;
TargetType operator()( const char* pszKey ) const { return MurmurHash3Functor<const char*>()( pszKey ); }
};
struct CaseSensitiveStrEquals
{
bool operator()( const char* pszLhs, const char* pszRhs ) const { return strcmp( pszLhs, pszRhs ) == 0; }
};
//-----------------------------------------------------------------------------
//
// Purpose: An associative container. Pretty much identical to CUtlMap without the ability to walk in-order
// This container is well suited for large and growing datasets. It uses less
// memory than other hash maps and incrementally rehashes to reduce reallocation spikes.
// However, it is slower (by about 20%) than CUtlHashTable
//
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L = CDefEquals<K>, typename H = MurmurHash3Functor<K> >
class CUtlHashMapLarge
{
public:
// This enum exists so that FOR_EACH_MAP and FOR_EACH_MAP_FAST cannot accidentally
// be used on a type that is not a CUtlMap. If the code compiles then all is well.
// The check for IsUtlMap being true should be free.
// Using an enum rather than a static const bool ensures that this trick works even
// with optimizations disabled on gcc.
enum CompileTimeCheck
{
IsUtlMap = 1
};
typedef K KeyType_t;
typedef T ElemType_t;
typedef int IndexType_t;
typedef L EqualityFunc_t;
typedef H HashFunc_t;
CUtlHashMapLarge()
{
m_cElements = 0;
m_nMaxElement = 0;
m_nMinRehashedBucket = InvalidIndex();
m_nMaxRehashedBucket = InvalidIndex();
m_iNodeFreeListHead = InvalidIndex();
}
CUtlHashMapLarge( int cElementsExpected )
{
m_cElements = 0;
m_nMaxElement = 0;
m_nMinRehashedBucket = InvalidIndex();
m_nMaxRehashedBucket = InvalidIndex();
m_iNodeFreeListHead = InvalidIndex();
EnsureCapacity( cElementsExpected );
}
~CUtlHashMapLarge()
{
RemoveAll();
}
// gets particular elements
ElemType_t & Element( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; }
const ElemType_t & Element( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; }
ElemType_t & operator[]( IndexType_t i ) { return m_memNodes.Element( i ).m_elem; }
const ElemType_t & operator[]( IndexType_t i ) const { return m_memNodes.Element( i ).m_elem; }
KeyType_t & Key( IndexType_t i ) { return m_memNodes.Element( i ).m_key; }
const KeyType_t & Key( IndexType_t i ) const { return m_memNodes.Element( i ).m_key; }
// Num elements
IndexType_t Count() const { return m_cElements; }
// Max "size" of the vector
IndexType_t MaxElement() const { return m_nMaxElement; }
// Checks if a node is valid and in the map
bool IsValidIndex( IndexType_t i ) const { return i >= 0 && i < m_nMaxElement && !IsFreeNodeID( m_memNodes[i].m_iNextNode ); }
// Invalid index
static IndexType_t InvalidIndex() { return -1; }
// Insert method
IndexType_t Insert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); }
IndexType_t Insert( const KeyType_t &key ) { return InsertInternal( key, ElemType_t(), eInsert_UpdateExisting ); }
IndexType_t InsertWithDupes( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_CreateDupes ); }
IndexType_t FindOrInsert( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_LeaveExisting ); }
IndexType_t InsertOrReplace( const KeyType_t &key, const ElemType_t &insert ) { return InsertInternal( key, insert, eInsert_UpdateExisting ); }
// Finds an element
IndexType_t Find( const KeyType_t &key ) const;
// has an element
bool HasElement( const KeyType_t &key ) const
{
return Find( key ) != InvalidIndex();
}
void EnsureCapacity( int num );
void RemoveAt( IndexType_t i );
bool Remove( const KeyType_t &key )
{
int iMap = Find( key );
if ( iMap != InvalidIndex() )
{
RemoveAt( iMap );
return true;
}
return false;
}
void RemoveAll();
void Purge();
void PurgeAndDeleteElements();
void Swap( CUtlHashMapLarge<K,T,L,H> &rhs )
{
m_vecHashBuckets.Swap( rhs.m_vecHashBuckets );
V_swap( m_bitsMigratedBuckets, rhs.m_bitsMigratedBuckets );
m_memNodes.Swap( rhs.m_memNodes );
V_swap( m_iNodeFreeListHead, rhs.m_iNodeFreeListHead );
V_swap( m_cElements, rhs.m_cElements );
V_swap( m_nMaxElement, rhs.m_nMaxElement );
V_swap( m_nMinRehashedBucket, rhs.m_nMinRehashedBucket );
V_swap( m_nMaxRehashedBucket, rhs.m_nMaxRehashedBucket );
V_swap( m_EqualityFunc, rhs.m_EqualityFunc );
V_swap( m_HashFunc, rhs.m_HashFunc );
}
private:
enum EInsertPolicy { eInsert_UpdateExisting, eInsert_LeaveExisting, eInsert_CreateDupes };
IndexType_t InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy );
inline IndexType_t FreeNodeIDToIndex( IndexType_t i ) const { return (0-i)-3; }
inline IndexType_t FreeNodeIndexToID( IndexType_t i ) const { return (-3)-i; }
inline bool IsFreeNodeID( IndexType_t i ) const { return i < InvalidIndex(); }
int FindInBucket( int iBucket, const KeyType_t &key ) const;
int AllocNode();
void RehashNodesInBucket( int iBucket );
void LinkNodeIntoBucket( int iBucket, int iNewNode );
void UnlinkNodeFromBucket( int iBucket, int iNewNode );
bool RemoveNodeFromBucket( int iBucket, int iNodeToRemove );
void IncrementalRehash();
struct HashBucket_t
{
IndexType_t m_iNode;
};
CUtlVector<HashBucket_t> m_vecHashBuckets;
CLargeVarBitVec m_bitsMigratedBuckets;
struct Node_t
{
KeyType_t m_key;
ElemType_t m_elem;
int m_iNextNode;
};
CUtlMemory<Node_t> m_memNodes;
IndexType_t m_iNodeFreeListHead;
IndexType_t m_cElements;
IndexType_t m_nMaxElement;
IndexType_t m_nMinRehashedBucket, m_nMaxRehashedBucket;
EqualityFunc_t m_EqualityFunc;
HashFunc_t m_HashFunc;
};
//-----------------------------------------------------------------------------
// Purpose: inserts an item into the map
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::InsertInternal( const KeyType_t &key, const ElemType_t &insert, EInsertPolicy ePolicy )
{
// make sure we have room in the hash table
if ( m_cElements >= m_vecHashBuckets.Count() )
EnsureCapacity( MAX( 16, m_vecHashBuckets.Count() * 2 ) );
if ( m_cElements >= m_memNodes.Count() )
m_memNodes.Grow( m_memNodes.Count() * 2 );
// rehash incrementally
IncrementalRehash();
// hash the item
uint32 hash = m_HashFunc( key );
// migrate data forward, if necessary
int cBucketsToModAgainst = m_vecHashBuckets.Count() >> 1;
int iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst);
while ( iBucket >= m_nMinRehashedBucket
&& !m_bitsMigratedBuckets.Get( iBucket ) )
{
RehashNodesInBucket( iBucket );
cBucketsToModAgainst >>= 1;
iBucket = basetypes::ModPowerOf2(hash, cBucketsToModAgainst);
}
// prevent duplicates if necessary
if ( ( ePolicy != eInsert_CreateDupes ) && m_cElements )
{
// look in the bucket to see if we have a conflict
int iBucket2 = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
IndexType_t iNode = FindInBucket( iBucket2, key );
if ( iNode != InvalidIndex() )
{
// a duplicate - update in place (matching CUtlMap)
if( ePolicy == eInsert_UpdateExisting )
{
m_memNodes[iNode].m_elem = insert;
}
return iNode;
}
}
// make an item
int iNewNode = AllocNode();
m_memNodes[iNewNode].m_iNextNode = InvalidIndex();
CopyConstruct( &m_memNodes[iNewNode].m_key, key );
CopyConstruct( &m_memNodes[iNewNode].m_elem, insert );
iBucket = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
// link ourselves in
// ::OutputDebugStr( CFmtStr( "insert %d into bucket %d\n", key, iBucket ).Access() );
LinkNodeIntoBucket( iBucket, iNewNode );
// return the new node
return iNewNode;
}
//-----------------------------------------------------------------------------
// Purpose: grows the map to fit the specified amount
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::EnsureCapacity( int amount )
{
m_memNodes.EnsureCapacity( amount );
// ::OutputDebugStr( CFmtStr( "grown m_memNodes from %d to %d\n", m_cElements, m_memNodes.Count() ).Access() );
if ( amount <= m_vecHashBuckets.Count() )
return;
int cBucketsNeeded = MAX( 16, m_vecHashBuckets.Count() );
while ( cBucketsNeeded < amount )
cBucketsNeeded *= 2;
// ::OutputDebugStr( CFmtStr( "grown m_vecHashBuckets from %d to %d\n", m_vecHashBuckets.Count(), cBucketsNeeded ).Access() );
// grow the hash buckets
int grow = cBucketsNeeded - m_vecHashBuckets.Count();
int iFirst = m_vecHashBuckets.AddMultipleToTail( grow );
// clear all the new data to invalid bits
memset( &m_vecHashBuckets[iFirst], 0xFFFFFFFF, grow*sizeof(m_vecHashBuckets[iFirst]) );
Assert( basetypes::IsPowerOf2( m_vecHashBuckets.Count() ) );
// we'll have to rehash, all the buckets that existed before growth
m_nMinRehashedBucket = 0;
m_nMaxRehashedBucket = iFirst;
if ( m_cElements > 0 )
{
// remove all the current bits
m_bitsMigratedBuckets.Resize( 0 );
// re-add new bits; these will all be reset to 0
m_bitsMigratedBuckets.Resize( m_vecHashBuckets.Count() );
}
else
{
// no elements - no rehashing
m_nMinRehashedBucket = m_vecHashBuckets.Count();
}
}
//-----------------------------------------------------------------------------
// Purpose: gets a new node, from the free list if possible
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::AllocNode()
{
// if we're out of free elements, get the max
if ( m_cElements == m_nMaxElement )
{
m_cElements++;
return m_nMaxElement++;
}
// pull from the free list
Assert( m_iNodeFreeListHead != InvalidIndex() );
int iNewNode = m_iNodeFreeListHead;
m_iNodeFreeListHead = FreeNodeIDToIndex( m_memNodes[iNewNode].m_iNextNode );
m_cElements++;
return iNewNode;
}
//-----------------------------------------------------------------------------
// Purpose: takes a bucket of nodes and re-hashes them into a more optimal bucket
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::RehashNodesInBucket( int iBucketSrc )
{
// mark us as migrated
m_bitsMigratedBuckets.Set( iBucketSrc );
// walk the list of items, re-hashing them
IndexType_t iNode = m_vecHashBuckets[iBucketSrc].m_iNode;
while ( iNode != InvalidIndex() )
{
IndexType_t iNodeNext = m_memNodes[iNode].m_iNextNode;
Assert( iNodeNext != iNode );
// work out where the node should go
const KeyType_t &key = m_memNodes[iNode].m_key;
uint32 hash = m_HashFunc( key );
int iBucketDest = basetypes::ModPowerOf2( hash, m_vecHashBuckets.Count() );
// if the hash bucket has changed, move it
if ( iBucketDest != iBucketSrc )
{
// ::OutputDebugStr( CFmtStr( "moved key %d from bucket %d to %d\n", key, iBucketSrc, iBucketDest ).Access() );
// remove from this bucket list
UnlinkNodeFromBucket( iBucketSrc, iNode );
// link into new bucket list
LinkNodeIntoBucket( iBucketDest, iNode );
}
iNode = iNodeNext;
}
}
//-----------------------------------------------------------------------------
// Purpose: searches for an item by key, returning the index handle
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::Find( const KeyType_t &key ) const
{
if ( m_cElements == 0 )
return InvalidIndex();
// hash the item
uint32 hash = m_HashFunc( key );
// find the bucket
int cBucketsToModAgainst = m_vecHashBuckets.Count();
int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
// look in the bucket for the item
int iNode = FindInBucket( iBucket, key );
if ( iNode != InvalidIndex() )
return iNode;
// not found? we may have to look in older buckets
cBucketsToModAgainst >>= 1;
while ( cBucketsToModAgainst >= m_nMinRehashedBucket )
{
iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
if ( !m_bitsMigratedBuckets.Get( iBucket ) )
{
int iNode2 = FindInBucket( iBucket, key );
if ( iNode2 != InvalidIndex() )
return iNode2;
}
cBucketsToModAgainst >>= 1;
}
return InvalidIndex();
}
//-----------------------------------------------------------------------------
// Purpose: searches for an item by key, returning the index handle
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline int CUtlHashMapLarge<K,T,L,H>::FindInBucket( int iBucket, const KeyType_t &key ) const
{
if ( m_vecHashBuckets[iBucket].m_iNode != InvalidIndex() )
{
IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode;
Assert( iNode < m_nMaxElement );
while ( iNode != InvalidIndex() )
{
// equality check
if ( m_EqualityFunc( key, m_memNodes[iNode].m_key ) )
return iNode;
iNode = m_memNodes[iNode].m_iNextNode;
}
}
return InvalidIndex();
}
//-----------------------------------------------------------------------------
// Purpose: links a node into a bucket
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
void CUtlHashMapLarge<K,T,L,H>::LinkNodeIntoBucket( int iBucket, int iNewNode )
{
// add into the start of the bucket's list
m_memNodes[iNewNode].m_iNextNode = m_vecHashBuckets[iBucket].m_iNode;
m_vecHashBuckets[iBucket].m_iNode = iNewNode;
}
//-----------------------------------------------------------------------------
// Purpose: unlinks a node from the bucket
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
void CUtlHashMapLarge<K,T,L,H>::UnlinkNodeFromBucket( int iBucket, int iNodeToUnlink )
{
int iNodeNext = m_memNodes[iNodeToUnlink].m_iNextNode;
// if it's the first node, just update the bucket to point to the new place
int iNode = m_vecHashBuckets[iBucket].m_iNode;
if ( iNode == iNodeToUnlink )
{
m_vecHashBuckets[iBucket].m_iNode = iNodeNext;
return;
}
// walk the list to find where
while ( iNode != InvalidIndex() )
{
if ( m_memNodes[iNode].m_iNextNode == iNodeToUnlink )
{
m_memNodes[iNode].m_iNextNode = iNodeNext;
return;
}
iNode = m_memNodes[iNode].m_iNextNode;
}
// should always be valid to unlink
Assert( false );
}
//-----------------------------------------------------------------------------
// Purpose: removes a single item from the map
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::RemoveAt( IndexType_t i )
{
if ( !IsValidIndex( i ) )
{
Assert( false );
return;
}
// unfortunately, we have to re-hash to find which bucket we're in
uint32 hash = m_HashFunc( m_memNodes[i].m_key );
int cBucketsToModAgainst = m_vecHashBuckets.Count();
int iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
if ( RemoveNodeFromBucket( iBucket, i ) )
return;
// wasn't found; look in older buckets
cBucketsToModAgainst >>= 1;
while ( cBucketsToModAgainst >= m_nMinRehashedBucket )
{
iBucket = basetypes::ModPowerOf2( hash, cBucketsToModAgainst );
if ( !m_bitsMigratedBuckets.Get( iBucket ) )
{
if ( RemoveNodeFromBucket( iBucket, i ) )
return;
}
cBucketsToModAgainst >>= 1;
}
// never found, container is busted
Assert( false );
}
//-----------------------------------------------------------------------------
// Purpose: removes a node from the bucket, return true if it was found
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline bool CUtlHashMapLarge<K,T,L,H>::RemoveNodeFromBucket( IndexType_t iBucket, int iNodeToRemove )
{
IndexType_t iNode = m_vecHashBuckets[iBucket].m_iNode;
while ( iNode != InvalidIndex() )
{
if ( iNodeToRemove == iNode )
{
// found it, remove
UnlinkNodeFromBucket( iBucket, iNodeToRemove );
Destruct( &m_memNodes[iNode].m_key );
Destruct( &m_memNodes[iNode].m_elem );
// link into free list
m_memNodes[iNode].m_iNextNode = FreeNodeIndexToID( m_iNodeFreeListHead );
m_iNodeFreeListHead = iNode;
m_cElements--;
if ( m_cElements == 0 )
{
m_nMinRehashedBucket = m_vecHashBuckets.Count();
}
return true;
}
iNode = m_memNodes[iNode].m_iNextNode;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: removes all items from the hash map
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::RemoveAll()
{
FOR_EACH_MAP_FAST( *this, i )
{
Destruct( &m_memNodes[i].m_key );
Destruct( &m_memNodes[i].m_elem );
}
m_cElements = 0;
m_nMaxElement = 0;
m_iNodeFreeListHead = InvalidIndex();
m_nMinRehashedBucket = m_vecHashBuckets.Count();
m_nMaxRehashedBucket = InvalidIndex();
m_bitsMigratedBuckets.Resize( 0 );
memset( m_vecHashBuckets.Base(), 0xFF, m_vecHashBuckets.Count() * sizeof(HashBucket_t) );
}
//-----------------------------------------------------------------------------
// Purpose: removes all items from the hash map and releases memory
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::Purge()
{
FOR_EACH_MAP_FAST( *this, i )
{
Destruct( &m_memNodes[i].m_key );
Destruct( &m_memNodes[i].m_elem );
}
m_cElements = 0;
m_nMaxElement = 0;
m_iNodeFreeListHead = InvalidIndex();
m_nMinRehashedBucket = InvalidIndex();
m_nMaxRehashedBucket = InvalidIndex();
m_bitsMigratedBuckets.Resize( 0 );
m_memNodes.Purge();
m_vecHashBuckets.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: removes and deletes all items from the hash map and releases memory
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::PurgeAndDeleteElements()
{
FOR_EACH_MAP_FAST( *this, i )
{
delete this->Element( i );
}
Purge();
}
//-----------------------------------------------------------------------------
// Purpose: rehashes buckets
//-----------------------------------------------------------------------------
template <typename K, typename T, typename L, typename H>
inline void CUtlHashMapLarge<K,T,L,H>::IncrementalRehash()
{
if ( m_nMinRehashedBucket < m_nMaxRehashedBucket )
{
while ( m_nMinRehashedBucket < m_nMaxRehashedBucket )
{
// see if the bucket needs rehashing
if ( m_vecHashBuckets[m_nMinRehashedBucket].m_iNode != InvalidIndex()
&& !m_bitsMigratedBuckets.Get(m_nMinRehashedBucket) )
{
// rehash this bucket
RehashNodesInBucket( m_nMinRehashedBucket );
// only actively do one - don't want to do it too fast since we may be on a rapid growth path
++m_nMinRehashedBucket;
break;
}
// nothing to rehash in that bucket - increment and look again
++m_nMinRehashedBucket;
}
if ( m_nMinRehashedBucket >= m_nMaxRehashedBucket )
{
// we're done; don't need any bits anymore
m_nMinRehashedBucket = m_vecHashBuckets.Count();
m_nMaxRehashedBucket = InvalidIndex();
m_bitsMigratedBuckets.Resize( 0 );
}
}
}
#endif // UTLHASHMAPLARGE_H