text: /*
       File: SA_Object.cpp 
   Abstract:  Part of SimpleAudioDriver Plug-In Example  
    Version: 1.0.1 
    
   Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple 
   Inc. ("Apple") in consideration of your agreement to the following 
   terms, and your use, installation, modification or redistribution of 
   this Apple software constitutes acceptance of these terms.  If you do 
   not agree with these terms, please do not use, install, modify or 
   redistribute this Apple software. 
    
   In consideration of your agreement to abide by the following terms, and 
   subject to these terms, Apple grants you a personal, non-exclusive 
   license, under Apple's copyrights in this original Apple software (the 
   "Apple Software"), to use, reproduce, modify and redistribute the Apple 
   Software, with or without modifications, in source and/or binary forms; 
   provided that if you redistribute the Apple Software in its entirety and 
   without modifications, you must retain this notice and the following 
   text and disclaimers in all such redistributions of the Apple Software. 
   Neither the name, trademarks, service marks or logos of Apple Inc. may 
   be used to endorse or promote products derived from the Apple Software 
   without specific prior written permission from Apple.  Except as 
   expressly stated in this notice, no other rights or licenses, express or 
   implied, are granted by Apple herein, including but not limited to any 
   patent rights that may be infringed by your derivative works or by other 
   works in which the Apple Software may be incorporated. 
    
   The Apple Software is provided by Apple on an "AS IS" basis.  APPLE 
   MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 
   THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 
   FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 
   OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
    
   IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 
   OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
   INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 
   MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 
   AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 
   STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 
   POSSIBILITY OF SUCH DAMAGE. 
    
   Copyright (C) 2013 Apple Inc. All Rights Reserved. 
    
  */
  /*==================================================================================================
  	SA_ObjectMap.cpp
  ==================================================================================================*/
  
  //==================================================================================================
  //	Includes
  //==================================================================================================
  
  //	Self Include
  #include "SA_Object.h"
  
  //	PublicUtility Includes
  #include "CADebugMacros.h"
  #include "CADispatchQueue.h"
  #include "CAException.h"
  
  //==================================================================================================
  #pragma mark -
  #pragma mark SA_Object
  //==================================================================================================
  
  #pragma mark Construction/Destruction
  
  SA_Object::SA_Object(AudioObjectID inObjectID, AudioClassID inClassID, AudioClassID inBaseClassID, AudioObjectID inOwnerObjectID)
  :
  	mObjectID(inObjectID),
  	mClassID(inClassID),
  	mBaseClassID(inBaseClassID),
  	mOwnerObjectID(inOwnerObjectID),
  	mIsActive(false)
  {
  }
  
  void	SA_Object::Activate()
  {
  	mIsActive = true;
  }
  
  void	SA_Object::Deactivate()
  {
  	mIsActive = false;
  }
  
  SA_Object::~SA_Object()
  {
  }
  
  #pragma mark Property Operations
  
  bool	SA_Object::HasProperty(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
  {
  	#pragma unused(inObjectID, inClientPID)
  	
  	bool theAnswer = false;
  	switch(inAddress.mSelector)
  	{
  		case kAudioObjectPropertyBaseClass:
  		case kAudioObjectPropertyClass:
  		case kAudioObjectPropertyOwner:
  		case kAudioObjectPropertyOwnedObjects:
  			theAnswer = true;
  			break;
  	};
  	return theAnswer;
  }
  
  bool	SA_Object::IsPropertySettable(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress) const
  {
  	#pragma unused(inObjectID, inClientPID)
  	
  	bool theAnswer = false;
  	switch(inAddress.mSelector)
  	{
  		case kAudioObjectPropertyBaseClass:
  		case kAudioObjectPropertyClass:
  		case kAudioObjectPropertyOwner:
  		case kAudioObjectPropertyOwnedObjects:
  			theAnswer = false;
  			break;
  		
  		default:
  			Throw(CAException(kAudioHardwareUnknownPropertyError));
  			break;
  	};
  	return theAnswer;
  }
  
  UInt32	SA_Object::GetPropertyDataSize(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
  {
  	#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData)
  	
  	UInt32 theAnswer = 0;
  	switch(inAddress.mSelector)
  	{
  		case kAudioObjectPropertyBaseClass:
  		case kAudioObjectPropertyClass:
  			theAnswer = sizeof(AudioClassID);
  			break;
  			
  		case kAudioObjectPropertyOwner:
  			theAnswer = sizeof(AudioObjectID);
  			break;
  			
  		case kAudioObjectPropertyOwnedObjects:
  			theAnswer = 0;
  			break;
  		
  		default:
  			Throw(CAException(kAudioHardwareUnknownPropertyError));
  			break;
  	};
  	return theAnswer;
  }
  
  void	SA_Object::GetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, UInt32& outDataSize, void* outData) const
  {
  	#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData)
  	
  	switch(inAddress.mSelector)
  	{
  		case kAudioObjectPropertyBaseClass:
  			//	This is the AudioClassID of the base class of this object. This is an invariant.
  			ThrowIf(inDataSize < sizeof(AudioClassID), CAException(kAudioHardwareBadPropertySizeError), "SA_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyBaseClass");
  			*reinterpret_cast<AudioClassID*>(outData) = mBaseClassID;
  			outDataSize = sizeof(AudioClassID);
  			break;
  			
  		case kAudioObjectPropertyClass:
  			//	This is the AudioClassID of the class of this object. This is an invariant.
  			ThrowIf(inDataSize < sizeof(AudioClassID), CAException(kAudioHardwareBadPropertySizeError), "SA_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyClass");
  			*reinterpret_cast<AudioClassID*>(outData) = mClassID;
  			outDataSize = sizeof(AudioClassID);
  			break;
  			
  		case kAudioObjectPropertyOwner:
  			//	The AudioObjectID of the object that owns this object. This is an invariant.
  			ThrowIf(inDataSize < sizeof(AudioObjectID), CAException(kAudioHardwareBadPropertySizeError), "SA_Object::GetPropertyData: not enough space for the return value of kAudioObjectPropertyOwner");
  			*reinterpret_cast<AudioClassID*>(outData) = mOwnerObjectID;
  			outDataSize = sizeof(AudioObjectID);
  			break;
  			
  		case kAudioObjectPropertyOwnedObjects:
  			//	This is an array of AudioObjectIDs for the objects owned by this object. By default,
  			//	objects don't own any other objects. This is an invariant by default, but an object
  			//	that can contain other objects will likely need to do some synchronization to access
  			//	this property.
  			outDataSize = 0;
  			break;
  		
  		default:
  			Throw(CAException(kAudioHardwareUnknownPropertyError));
  			break;
  	};
  }
  
  void	SA_Object::SetPropertyData(AudioObjectID inObjectID, pid_t inClientPID, const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
  {
  	#pragma unused(inObjectID, inClientPID, inQualifierDataSize, inQualifierData, inDataSize, inData)
  	
  	switch(inAddress.mSelector)
  	{
  		default:
  			Throw(CAException(kAudioHardwareUnknownPropertyError));
  			break;
  	};
  }
  
  //==================================================================================================
  #pragma mark -
  #pragma mark SA_ObjectMap
  //==================================================================================================
  
  #pragma mark Construction/Destruction
  
  SA_ObjectMap::SA_ObjectMap()
  :
  	mMutex("SA_ObjectMap Mutex"),
  	mNextObjectID(32),
  	mObjectInfoList()
  {
  	mObjectInfoList.reserve(256);
  }
  
  SA_ObjectMap::~SA_ObjectMap()
  {
  }
  
  void	SA_ObjectMap::StaticInitializer()
  {
  	sInstance = new SA_ObjectMap;
  	AssertNotNULL(sInstance, "SA_ObjectMap::StaticInitializer: failed to create the object map");
  }
  
  #pragma mark External Methods
  
  AudioObjectID	SA_ObjectMap::GetNextObjectID()
  {
  	pthread_once(&sStaticInitializer, StaticInitializer);
  	CAMutex::Locker theLocker(sInstance->mMutex);
  	AudioObjectID theAnswer = sInstance->_GetNextObjectID();
  	return theAnswer;
  }
  
  bool	SA_ObjectMap::MapObject(AudioObjectID inObjectID, SA_Object* inObject)
  {
  	pthread_once(&sStaticInitializer, StaticInitializer);
  	bool theAnswer = false;
  	if((inObjectID != 0) && (inObject != NULL))
  	{
  		CAMutex::Locker theLocker(sInstance->mMutex);
  		theAnswer = sInstance->_MapObject(inObjectID, inObject);
  	}
  	return theAnswer;
  }
  
  void	SA_ObjectMap::UnmapObject(AudioObjectID inObjectID, SA_Object* inObject)
  {
  	pthread_once(&sStaticInitializer, StaticInitializer);
  	if((inObjectID != 0) && (inObject != NULL))
  	{
  		CAMutex::Locker theLocker(sInstance->mMutex);
  		sInstance->_UnmapObject(inObjectID, inObject);
  	}
  }
  
  SA_Object*	SA_ObjectMap::CopyObjectByObjectID(AudioObjectID inObjectID)
  {
  	pthread_once(&sStaticInitializer, StaticInitializer);
  	SA_Object* theAnswer = NULL;
  	if(inObjectID != 0)
  	{
  		CAMutex::Locker theLocker(sInstance->mMutex);
  		theAnswer = sInstance->_CopyObjectByObjectID(inObjectID);
  	}
  	return theAnswer;
  }
  
  UInt64	SA_ObjectMap::RetainObject(SA_Object* inObject)
  {
  	pthread_once(&sStaticInitializer, StaticInitializer);
  	UInt64 theAnswer = 0;
  	if(inObject != NULL)
  	{
  		CAMutex::Locker theLocker(sInstance->mMutex);
  		theAnswer = sInstance->_RetainObject(inObject);
  	}
  	return theAnswer;
  }
  
  UInt64	SA_ObjectMap::ReleaseObject(SA_Object* inObject)
  {
  	pthread_once(&sStaticInitializer, StaticInitializer);
  	UInt64 theAnswer = 0;
  	if(inObject != NULL)
  	{
  		CAMutex::Locker theLocker(sInstance->mMutex);
  		theAnswer = sInstance->_ReleaseObject(inObject);
  	}
  	return theAnswer;
  }
  
  void	SA_ObjectMap::Dump()
  {
  	pthread_once(&sStaticInitializer, StaticInitializer);
  	CAMutex::Locker theLocker(sInstance->mMutex);
  	sInstance->_Dump();
  }
  
  void	SA_ObjectMap::DestroyObject(SA_Object* inObject)
  {
  	//	The method is called when an object has been fully released and removed from the map. There
  	//	is no need to synchronize against the object map's mutex. All it needs to do is dispose of
  	//	the object in whatever manner is most suitable.
  	delete inObject;
  }
  
  #pragma mark Internal Methods
  
  AudioObjectID	SA_ObjectMap::_GetNextObjectID()
  {
  	return mNextObjectID++;
  }
  
  bool	SA_ObjectMap::_MapObject(AudioObjectID inObjectID, SA_Object* inObject)
  {
  	bool theAnswer = false;
  	
  	//	we don't do mappings for IDs of 0 or NULL object pointers
  	if((inObjectID != 0) && (inObject != NULL))
  	{
  		//	look to see if the ID is already attached to an object
  		ObjectInfoList::iterator theByIDIterator = std::find(mObjectInfoList.begin(), mObjectInfoList.end(), inObjectID);
  		if(theByIDIterator == mObjectInfoList.end())
  		{
  			//	it is not, so we're going to do a mapping
  			theAnswer = true;
  			
  			//	look to see if the object is already in the list
  			ObjectInfoList::iterator theByPtrIterator = std::find(mObjectInfoList.begin(), mObjectInfoList.end(), inObject);
  			if(theByPtrIterator != mObjectInfoList.end())
  			{
  				//	it is, so just add the new ID to it's ID list
  				theByPtrIterator->mObjectIDList.push_back(inObjectID);
  			}
  			else
  			{
  				//	this is the first time this object has been mapped, so add a new entry to the list
  				mObjectInfoList.push_back(ObjectInfo(inObjectID, inObject));
  			}
  		}
  		else
  		{
  			//	the given ID is already attached to an object, this is a programming error
  			DebugMsg("HALB_ObjectMap::_MapObject: %d cannot be mapped to object %p because it is already mapped to %p", (int)inObjectID, inObject, theByIDIterator->mObject);
  		}
  	}
  	
  	return theAnswer;
  }
  
  void	SA_ObjectMap::_UnmapObject(AudioObjectID inObjectID, SA_Object* inObject)
  {
  	//	we don't do mappings for IDs of 0 or NULL object pointers
  	if((inObjectID != 0) && (inObject != NULL))
  	{
  		//	find the object this ID is attached to
  		ObjectInfoList::iterator theIterator = std::find(mObjectInfoList.begin(), mObjectInfoList.end(), inObjectID);
  		if(theIterator != mObjectInfoList.end())
  		{
  			//	make sure that it is the object we expect to be unmapping
  			if(theIterator->mObject == inObject)
  			{
  				//	find the ID in the ID list
  				ObjectIDList::iterator theIDIterator = std::find(theIterator->mObjectIDList.begin(), theIterator->mObjectIDList.end(), inObjectID);
  				if(theIDIterator != theIterator->mObjectIDList.end())
  				{
  					//	get rid of it
  					theIterator->mObjectIDList.erase(theIDIterator);
  					
  					//	get rid of the object if there are no more IDs
  					if(theIterator->mObjectIDList.empty())
  					{
  						//	get rid of the info in the list
  						mObjectInfoList.erase(theIterator);
  						
  						//	and destroy the object
  						CADispatchQueue::GetGlobalSerialQueue().Dispatch(false, ^{ DestroyObject(inObject); });
  					}
  				}
  			}
  		}
  	}
  }
  
  SA_Object*	SA_ObjectMap::_CopyObjectByObjectID(AudioObjectID inObjectID)
  {
  	SA_Object* theAnswer = NULL;
  	
  	//	find the object this ID is attached to
  	ObjectInfoList::iterator theIterator = std::find(mObjectInfoList.begin(), mObjectInfoList.end(), inObjectID);
  	if(theIterator != mObjectInfoList.end())
  	{
  		//	don't overflow the reference count
  		if(theIterator->mReferenceCount < UINT64_MAX)
  		{
  			//	increment the reference count
  			theIterator->mReferenceCount += 1;
  			
  			//	return the object pointer
  			theAnswer = theIterator->mObject;
  		}
  		else
  		{
  			DebugMsg("SA_ObjectMap::_CopyObjectByObjectID: not copying because the reference count is at maximum");
  		}
  	}
  	
  	return theAnswer;
  }
  
  UInt64	SA_ObjectMap::_RetainObject(SA_Object* inObject)
  {
  	UInt64 theAnswer = 0;
  
  	//	find the info for this object
  	ObjectInfoList::iterator theIterator = std::find(mObjectInfoList.begin(), mObjectInfoList.end(), inObject);
  	if(theIterator != mObjectInfoList.end())
  	{
  		//	don't overflow the reference count
  		if(theIterator->mReferenceCount < UINT64_MAX)
  		{
  			//	increment the reference count
  			theIterator->mReferenceCount += 1;
  		}
  		else
  		{
  			DebugMsg("SA_ObjectMap::_RetainObject: not retaining because the reference count is at maximum");
  		}
  		
  		theAnswer = theIterator->mReferenceCount;
  	}
  	
  	return theAnswer;
  }
  
  UInt64	SA_ObjectMap::_ReleaseObject(SA_Object* inObject)
  {
  	UInt64 theAnswer = 0;
  
  	//	find the info for this object
  	ObjectInfoList::iterator theIterator = std::find(mObjectInfoList.begin(), mObjectInfoList.end(), inObject);
  	if(theIterator != mObjectInfoList.end())
  	{
  		//	don't underflow the reference count
  		if(theIterator->mReferenceCount > 0)
  		{
  			//	decrement the reference count
  			theIterator->mReferenceCount -= 1;
  			
  			//	destroy the object if the count reaches 0
  			if(theIterator->mReferenceCount == 0)
  			{
  				//	get rid of the info in the list
  				mObjectInfoList.erase(theIterator);
  				
  				//	and destroy the object
  				CADispatchQueue::GetGlobalSerialQueue().Dispatch(false, ^{ DestroyObject(inObject); });
  			}
  		}
  		else
  		{
  			DebugMsg("SA_ObjectMap::_ReleaseObject: not releasing because the reference count is already at 0");
  		}
  		
  		theAnswer = theIterator->mReferenceCount;
  	}
  	
  	return theAnswer;
  }
  
  void	SA_ObjectMap::_Dump()
  {
  
  	//	iterate through the objects in the map and dump their id, class, and ref count
  	DebugMsg("HALB_ObjectMap::_Dump:");
  	AudioClassID	theBaseClassID;
  	char			theBaseClassIDString[5];
  	AudioClassID	theClassID;
  	char			theClassIDString[5];
  	UInt64			theReferenceCount;
  	
  	if(!mObjectInfoList.empty())
  	{
  		for(ObjectInfoList::iterator theIterator = mObjectInfoList.begin(); theIterator != mObjectInfoList.end(); ++theIterator)
  		{
  			theBaseClassID = theIterator->mObject->GetBaseClassID();
  			theClassID = theIterator->mObject->GetClassID();
  			theReferenceCount = theIterator->mReferenceCount;
  			CACopy4CCToCString(theBaseClassIDString, theBaseClassID);
  			CACopy4CCToCString(theClassIDString, theClassID);
  			
  			if(theIterator->mObjectIDList.size() == 1)
  			{
  				DebugMsg("  Object: %p | Class: '%s' | Base Class: '%s' | Ref: %4qd | ID: %d", theIterator->mObject, theClassIDString, theBaseClassIDString, theReferenceCount, (int)theIterator->mObjectIDList.front());
  			}
  			else
  			{
  				DebugMsg("  Object: %p | Class: '%s' | Base Class: '%s' | Ref: %4qd | Number IDs: %d", theIterator->mObject, theClassIDString, theBaseClassIDString, theReferenceCount, (int)theIterator->mObjectIDList.size());
  			
  				for(size_t theIndex = 0; theIndex < theIterator->mObjectIDList.size(); ++theIndex)
  				{
  					DebugMsg("    ID %3d: %d", (int)theIndex, (int)theIterator->mObjectIDList.at(theIndex));
  				}
  			}
  		}
  	}
  	else
  	{
  		DebugMsg("  No Objects");
  	}
  }
  
  pthread_once_t	SA_ObjectMap::sStaticInitializer = PTHREAD_ONCE_INIT;
  SA_ObjectMap*	SA_ObjectMap::sInstance = NULL;
  