mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-14 20:56:56 +00:00
1
This commit is contained in:
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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_
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user