//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // The use and distribution terms for this software are covered by the // Microsoft Limited Public License (Ms-LPL) // which can be found in the file MS-LPL.txt at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. // // The software is licensed “as-is.” // // You must not remove this notice, or any other, from this software. // // // // Demux code for Windows CE // //------------------------------------------------------------------------- //====================================================================== // Demux code for Windows CE //====================================================================== /*++ Module Name: demxutil.h Abstract: This module contains the utilities, etc... that are used throughout the demux Revision History: 27-Aug-1999 created 07-Jan-2000 added LIST_ENTRY macros added LIST_ENTRY_CONTAINER template struct added TListEntryContainerPool template class added BSearch template function added BSearchBracket template function 25-Jan-2000 added CMovingAverage object 11-May-2000 rewrote CGenericMap, CGenericMultimap to remove STL dependency added CGenericSingleMap Notes: --*/ #ifndef __mp2demux_demxutil_h #define __mp2demux_demxutil_h #ifdef UNDER_CE #ifdef HEAP_GENERATE_EXCEPTIONS #undef HEAP_GENERATE_EXCEPTIONS #endif #define HEAP_GENERATE_EXCEPTIONS 0 #endif // UNDER_CE // prototypes LONG RegGetValIfExist ( IN HKEY hkeyRoot, IN LPCTSTR szValName, IN BOOL fCreateKey, OUT DWORD * pdw ) ; WCHAR * ToUnicode ( IN TCHAR * sztInTCHARString, IN WCHAR * szwInWCHARString, IN DWORD dwWCHARStringMax ) ; LPWSTR AnsiToUnicode ( IN LPCSTR string, OUT LPWSTR buffer, IN DWORD buffer_len ) ; LPSTR UnicodeToAnsi ( IN LPCWSTR string, OUT LPSTR buffer, IN DWORD buffer_len ) ; HRESULT EnableStats ( IN TCHAR * pszRegRoot, IN OUT BOOL * pfEnable ) ; BOOL StatsEnabled ( IN TCHAR * pszRegRoot ) ; // list macros (defined in ntrtl.h) // // VOID // InitializeListHead( // PLIST_ENTRY ListHead // ); // #ifndef InitializeListHead #define InitializeListHead(ListHead) (\ (ListHead)->Flink = (ListHead)->Blink = (ListHead)) #endif // InitializeListHead // // BOOLEAN // IsListEmpty( // PLIST_ENTRY ListHead // ); // #ifndef IsListEmpty #define IsListEmpty(ListHead) \ ((ListHead)->Flink == (ListHead)) #endif // IsListEmpty // // PLIST_ENTRY // RemoveHeadList( // PLIST_ENTRY ListHead // ); // #ifndef RemoveHeadList #define RemoveHeadList(ListHead) \ (ListHead)->Flink;\ {RemoveEntryList((ListHead)->Flink)} #endif // RemoveHeadList // // PLIST_ENTRY // RemoveTailList( // PLIST_ENTRY ListHead // ); // #ifndef RemoveTailList #define RemoveTailList(ListHead) \ (ListHead)->Blink;\ {RemoveEntryList((ListHead)->Blink)} #endif // RemoveTailList // // VOID // RemoveEntryList( // PLIST_ENTRY Entry // ); // #ifndef RemoveEntryList #define RemoveEntryList(Entry) {\ PLIST_ENTRY _EX_Blink;\ PLIST_ENTRY _EX_Flink;\ _EX_Flink = (Entry)->Flink;\ _EX_Blink = (Entry)->Blink;\ _EX_Blink->Flink = _EX_Flink;\ _EX_Flink->Blink = _EX_Blink;\ } #endif // RemoveEntryList // // VOID // InsertTailList( // PLIST_ENTRY ListHead, // PLIST_ENTRY Entry // ); // #ifndef InsertTailList #define InsertTailList(ListHead,Entry) {\ PLIST_ENTRY _EX_Blink;\ PLIST_ENTRY _EX_ListHead;\ _EX_ListHead = (ListHead);\ _EX_Blink = _EX_ListHead->Blink;\ (Entry)->Flink = _EX_ListHead;\ (Entry)->Blink = _EX_Blink;\ _EX_Blink->Flink = (Entry);\ _EX_ListHead->Blink = (Entry);\ } #endif // InsertTailList // // VOID // InsertHeadList( // PLIST_ENTRY ListHead, // PLIST_ENTRY Entry // ); // #ifndef InsertHeadList #define InsertHeadList(ListHead,Entry) {\ PLIST_ENTRY _EX_Flink;\ PLIST_ENTRY _EX_ListHead;\ _EX_ListHead = (ListHead);\ _EX_Flink = _EX_ListHead->Flink;\ (Entry)->Flink = _EX_Flink;\ (Entry)->Blink = _EX_ListHead;\ _EX_Flink->Blink = (Entry);\ _EX_ListHead->Flink = (Entry);\ } #endif // InsertHeadList // // inserts NewListEntry after ListEntry // // VOID // InsertListEntry ( // PLIST_ENTRY ListEntry, // PLIST_ENTRY NewListEntry // ) ; // #define InsertListEntry(ListEntry,Entry) {\ PLIST_ENTRY _EX_Flink;\ PLIST_ENTRY _EX_ListEntry;\ _EX_ListEntry = (ListEntry);\ _EX_Flink = _EX_ListEntry->Flink;\ (Entry)->Flink = _EX_Flink;\ (Entry)->Blink = _EX_ListEntry;\ _EX_Flink->Blink = (Entry);\ _EX_ListEntry->Flink = (Entry);\ } // // moves entire contents of FromListHead list to ToListHead list, // preserving order // // VOID // MoveEntryList ( // PLIST_ENTRY FromListHead, // PLIST_ENTRY ToListHead // ) ; // #define MoveEntryList(FromListHead,ToListHead) {\ PLIST_ENTRY _EX_FromListHead;\ PLIST_ENTRY _EX_ToListHead;\ _EX_FromListHead = (FromListHead);\ _EX_ToListHead = (ToListHead);\ _EX_ToListHead->Flink = _EX_FromListHead->Flink;\ _EX_ToListHead->Blink = _EX_FromListHead->Blink;\ _EX_ToListHead->Flink->Blink = _EX_ToListHead;\ _EX_ToListHead->Blink->Flink = _EX_ToListHead;\ (FromListHead)->Flink = (FromListHead)->Blink = (FromListHead);\ } // // // PSINGLE_LIST_ENTRY // PopEntryList( // PSINGLE_LIST_ENTRY ListHead // ); // #ifndef PopEntryList #define PopEntryList(ListHead) \ (ListHead)->Next;\ {\ PSINGLE_LIST_ENTRY FirstEntry;\ FirstEntry = (ListHead)->Next;\ if (FirstEntry != NULL) { \ (ListHead)->Next = FirstEntry->Next;\ } \ } #endif // PopEntryList // // VOID // PushEntryList( // PSINGLE_LIST_ENTRY ListHead, // PSINGLE_LIST_ENTRY Entry // ); // #ifndef PushEntryList #define PushEntryList(ListHead,Entry) \ (Entry)->Next = (ListHead)->Next; \ (ListHead)->Next = (Entry) #endif // PushEntryList // --------------------------------------------------------------------------- // templates template T Min (T a, T b) { return (a < b ? a : b) ; } template T Max (T a, T b) { return (a > b ? a : b) ; } template T Abs (T t) { return (t >= 0 ? t : -t) ; } // --------------------------------------------------------------------------- // CIAsyncReader // --------------------------------------------------------------------------- template class CIAsyncReader /*++ Wraps an IAsyncReader COM interface pointer. Moves a window along the file returning a BYTE * pointer with valid length. --*/ { LONGLONG m_llFileTotalLength ; BYTE m_Buffer [lBufferLength] ; LONGLONG m_llBufferOffset ; LONG m_lBufferValid ; IAsyncReader * m_pIAsyncReader ; LONG Read_ ( IN LONGLONG llFileOffset ) { HRESULT hr ; #ifndef UNDER_CE LONGLONG llAvailable ; LONGLONG llTotal ; #endif //UNDER_CE LONG lRead ; // should have already verified that the offset is valid ASSERT (llFileOffset < m_llFileTotalLength) ; // we may have more buffer left than file; lRead = (LONG) Min (m_llFileTotalLength - llFileOffset, lBufferLength) ; // need to read something in if (lRead > 0) { hr = m_pIAsyncReader -> SyncRead ( llFileOffset, lRead, m_Buffer ) ; if (SUCCEEDED (hr)) { // update member variables m_lBufferValid = lRead ; m_llBufferOffset = llFileOffset ; } else { lRead = 0 ; } } else { // ran out of file lRead = 0 ; } return lRead ; } BOOL InCurrentWindow_ ( IN LONGLONG llFileOffset, IN OUT LONG * plLength ) { BOOL r ; #ifndef UNDER_CE LONG lBufferRemaining ; #endif //UNDER_CE // if we have a window r = HaveWindow_ () ; if (r) { // check the starting offset if (llFileOffset >= m_llBufferOffset) { // check endpoint; may need to shrink the length (* plLength) = (LONG) Max (m_lBufferValid - (llFileOffset - m_llBufferOffset), 0) ; r = ((* plLength) > 0) ; } else { r = FALSE ; } } return r ; } BOOL HaveWindow_ ( ) { return (m_llBufferOffset != UNDEFINED ? TRUE : FALSE) ; } public : CIAsyncReader ( IN IAsyncReader * pIAsyncReader ) : m_pIAsyncReader (pIAsyncReader), m_llFileTotalLength (0), m_llBufferOffset (UNDEFINED), m_lBufferValid (0) { ASSERT (m_pIAsyncReader) ; m_pIAsyncReader -> AddRef () ; } virtual ~CIAsyncReader ( ) { m_pIAsyncReader -> Release () ; } LONGLONG GetFileTotalLength ( ) { HRESULT hr ; LONGLONG llAvailable ; if (m_llFileTotalLength == 0) { hr = m_pIAsyncReader -> Length ( & m_llFileTotalLength, & llAvailable ) ; if (FAILED (hr)) { // make sure it's still undefined m_llFileTotalLength = 0 ; } } return m_llFileTotalLength ; } // assumption is that we are moving forward; so if there's a read // it will at the specified offset BYTE * const SetWindow ( IN LONGLONG llFileOffset, IN OUT LONG * plWindowLength // IN = 0 -> read in max/fill in // what's available ) { LONGLONG llOffset ; LONG lLength ; BYTE * pb ; BOOL r ; ASSERT (plWindowLength) ; if (GetFileTotalLength () > llFileOffset) { // valid request - window starts within the file // length is either specified, or want to have max lLength = ((* plWindowLength) == 0 ? lBufferLength : (* plWindowLength)) ; r = InCurrentWindow_ ( llFileOffset, & lLength ) ; if (!r || ((* plWindowLength) > 0 && lLength < (* plWindowLength))) { // either the starting point is out of bounds, or the caller // specified a length and it's not within the window; // either way we need to read something in llOffset = llFileOffset ; lLength = lBufferLength ; // try to read as much as possible lLength = Read_ (llOffset) ; if (lLength == 0) { // failed to read anything in return NULL ; } } ASSERT (llFileOffset >= m_llBufferOffset) ; ASSERT (llFileOffset + lLength <= m_llBufferOffset + m_lBufferValid) ; // set outgoing pb = & (m_Buffer [0]) + (llFileOffset - m_llBufferOffset) ; (* plWindowLength) = lLength ; } else { // invalid request pb = NULL ; } return pb ; } BYTE * const GetCurrentWindow ( OUT LONGLONG * pllFileOffset, OUT LONG * plWindowLength ) { BYTE * pb ; if (m_llBufferOffset != UNDEFINED) { pb = & (m_Buffer [0]) ; (* pllFileOffset) = m_llBufferOffset ; (* plWindowLength) = m_lBufferValid ; } else { pb = NULL ; } return pb ; } } ; // --------------------------------------------------------------------------- // CMpeg2ProgramStreamSniffer // --------------------------------------------------------------------------- class CMpeg2ProgramStreamSniffer /*++ Performs program stream parsing on an IAsyncReader pointer. Allows frame seeking, frame stepping. Also provides static helper methods to determine frame length, header length, etc.. --*/ { enum { // -------------------------------------------------------------------- // this is the read buffer size; we read multiple of these obviously, // but we keep this small so we don't have drag MBs of data all over // the place like the old one did SCRATCH_BUFFER_LENGTH = MAX_PS_PES_PACKET_LENGTH + 3 } ; CIAsyncReader m_CIAsyncReader ; public : CMpeg2ProgramStreamSniffer ( IN IAsyncReader * pIAsyncReader ) : m_CIAsyncReader (pIAsyncReader) {} virtual ~CMpeg2ProgramStreamSniffer ( ) {} // -------------------------------------------------------------------- // static helper methods static LONG PESHeaderLength (IN BYTE * pbPESHeader) ; static LONG PESPacketLength (IN BYTE * pbPESPacket) ; static LONGLONG PackSCR (IN BYTE * pbPackHeader) ; static DWORD PackMuxRate (IN BYTE * pbPackHeader) ; static BOOL IsVideoStartCode (IN BYTE bStartCode) { return (STREAM_ID_VIDEO_STREAM_MIN <= bStartCode && bStartCode <= STREAM_ID_VIDEO_STREAM_MAX) ; } static BOOL IsAudioStartCode (IN BYTE bStartCode) { return (STREAM_ID_AUDIO_STREAM_MIN <= bStartCode && bStartCode <= STREAM_ID_AUDIO_STREAM_MAX) ; } static BOOL IsPrivateStream1 (IN BYTE bStartCode) { return (bStartCode == STREAM_ID_PRIVATE_STREAM_1) ; } // -------------------------------------------------------------------- LONGLONG ProgramStreamLength () { return m_CIAsyncReader.GetFileTotalLength () ; } // -------------------------------------------------------------------- // llStartingOffset may/may not be on a prefix boundary HRESULT SeekToNextPacket ( IN OUT LONGLONG * pllStreamOffset, // IN: [start; OUT: found OUT BYTE ** ppbPESPacket, // pointer to a buffer with the packet OUT LONG * plBufferLength // available buffer ) ; // llStartingOffset must be on a prefix boundary; fails if the // stepping forward by the length of the PES packet (as specified // in the PES header), does not point to a prefix boundary HRESULT StepToNextPacketStrict ( IN OUT LONGLONG * pllStreamOffset, // IN: [start; OUT: found OUT BYTE ** ppbPESPacket, // pointer to a buffer with the packet OUT LONG * plBufferLength // available buffer ) ; // llStartingOffset must be on a prefix boundary; does not fail if // stepping forward by the length of the PES packet (as specified // in the PES header), does not point to a prefix boundary; if it does // not, a seek forward to the next prefix is made; the call if fails // if no prefix is found anywhere HRESULT StepToNextPacketNonStrict ( IN OUT LONGLONG * pllStreamOffset, // IN: [start; OUT: found OUT BYTE ** ppbPESPacket, // pointer to a buffer with the packet OUT LONG * plBufferLength // available buffer ) ; } ; // --------------------------------------------------------------------------- // CRefCountedCritSec // --------------------------------------------------------------------------- class CRefCountedCritSec : public CCritSec /*++ Independent object that lives/dies by refcounts. We use it when we have disparate refcounted objects that must make use of a common lock for synchronization purposes. --*/ { LONG m_cRefcount ; public : CRefCountedCritSec ( ) : m_cRefcount (1) { TRACE_CONSTRUCTOR (TEXT ("CRefCountedCritSec")) ; } ~CRefCountedCritSec ( ) { TRACE_DESTRUCTOR (TEXT ("CRefCountedCritSec")) ; } ULONG AddRef ( ) { return InterlockedIncrement (& m_cRefcount) ; } ULONG Release ( ) { if (InterlockedDecrement (& m_cRefcount) == 0) { delete this ; return 0 ; } ASSERT (m_cRefcount > 0) ; return m_cRefcount ; } } ; // ---------------------------------------------------------------------------- // CTStickyVal // ---------------------------------------------------------------------------- template class CTStickyVal /*++ This class stores a 1-way value i.e. once the value is set, it cannot be set to another value without resetting. The sticky value is specified when the object is created. This allows various callers to set the value, but if any of them sets the the value, subsequents settings will not erase the sticky value that was set previously. --*/ { T m_StickyVal ; T m_CurrentVal ; public : CTStickyVal ( IN T StickyVal ) : m_StickyVal (StickyVal), m_CurrentVal (StickyVal) {} void Init (IN T tVal) { m_CurrentVal = tVal ; } void Set (IN T tVal) { m_CurrentVal = (m_CurrentVal == m_StickyVal ? m_CurrentVal : tVal) ; } BOOL IsSet () { return (m_CurrentVal == m_StickyVal ? TRUE : FALSE) ; } } ; // ---------------------------------------------------------------------------- // CDataCache // ---------------------------------------------------------------------------- class CDataCache { BYTE * m_pbCache ; BYTE * m_pbCurrent ; int m_iCacheSize ; public : CDataCache ( IN BYTE * pbCache, IN int iCacheSize ) : m_pbCache (pbCache), m_iCacheSize (iCacheSize), m_pbCurrent (pbCache) {} int CurCacheSize () { return (int) (m_pbCurrent - m_pbCache) ; } int CacheRemaining () { return m_iCacheSize - CurCacheSize () ; } BYTE * Get () { return m_pbCache ; } void Reset () { m_pbCurrent = m_pbCache ; } BOOL IsEmpty () { return CurCacheSize () == 0 ; } BOOL Append ( IN BYTE * pb, IN int iLength ) { if (iLength <= CacheRemaining ()) { CopyMemory ( m_pbCurrent, pb, iLength ) ; m_pbCurrent += iLength ; return TRUE ; } else { return FALSE ; } } } ; template class TSizedDataCache : public CDataCache { BYTE m_pbCache [iCacheSize] ; public : TSizedDataCache ( ) : CDataCache ( m_pbCache, iCacheSize ) {} } ; // ---------------------------------------------------------------------------- // lookup table // ---------------------------------------------------------------------------- template class TLookupTableBase { protected : struct LOOKUP_RECORD { DWORD dwFilterIndex ; // index for the lookup record T Object ; // storage } ; private : BYTE * m_pFilter ; LOOKUP_RECORD * m_pLookup ; DWORD m_cLookup ; DWORD m_dwLookupSize ; DWORD m_dwMax ; public : TLookupTableBase ( IN DWORD dwLookupSize, IN DWORD dwMax, IN BYTE * pbFilter, IN LOOKUP_RECORD * pLookup ) : m_dwLookupSize (dwLookupSize), m_dwMax (dwMax), m_cLookup (0), m_pFilter (pbFilter), m_pLookup (pLookup) { ASSERT (dwMax <= MAXBYTE) ; ZeroMemory (m_pFilter, m_dwLookupSize * sizeof BYTE) ; ZeroMemory (m_pLookup, m_dwMax * sizeof LOOKUP_RECORD) ; } ~TLookupTableBase ( ) { ASSERT (m_cLookup == 0) ; } HRESULT Add ( IN DWORD dwId, IN T t ) { ASSERT (dwId < m_dwLookupSize) ; ASSERT (Exists (dwId) == FALSE) ; if (m_cLookup < m_dwMax) { // set the indirect fields up m_pLookup [m_cLookup].Object = t ; m_pLookup [m_cLookup].dwFilterIndex = dwId ; // and the filter m_pFilter [dwId] = (BYTE) (m_cLookup + 1) ; // 1-based index; 0 means there's no Lookup // one more Lookup m_cLookup++ ; // success return S_OK ; } else { // no more room left return E_FAIL ; } } T Remove ( IN DWORD dwId ) { T tRet ; DWORD RemoveIndex ; if (Exists (dwId)) { // save off the index of the Lookup we are deleting RemoveIndex = m_pFilter [dwId] - 1 ; // get a pointer to the context tRet = Get (dwId) ; // erase the Lookupping m_pFilter [dwId] = 0 ; // ------------------------------------------------------------------- // we want all the entries in m_pLookup to be densely packed, // so we fill the slot we just opened, by moving the last entry into // it // only copy if we didn't remove the last one if (RemoveIndex < m_cLookup - 1) { // move the last Lookup down (m_cLookup indexes the next // slot slated to be filled, so we subtract 1) m_pLookup [RemoveIndex].Object = m_pLookup [m_cLookup - 1].Object ; m_pLookup [RemoveIndex].dwFilterIndex = m_pLookup [m_cLookup - 1].dwFilterIndex ; // update the filter to the new index (1-based) m_pFilter [m_pLookup [RemoveIndex].dwFilterIndex] = (BYTE) (RemoveIndex + 1) ; } // last, update the counter/index m_cLookup-- ; return tRet ; } else { return 0 ; } } BOOL Exists ( IN DWORD dwId ) { ASSERT (dwId < m_dwLookupSize) ; return m_pFilter [dwId] != 0 ; } T Get ( IN DWORD dwId ) { return Exists (dwId) ? m_pLookup [m_pFilter [dwId] - 1].Object : 0 ; } T operator [] ( IN DWORD dwId ) { return Get (dwId) ; } T GetIndexed ( IN DWORD dwIndex ) // returns the dwIndex (0-based) object, or NULL if dwIndex is beyond // the scope of what's in the table { if (dwIndex < m_cLookup) { return m_pLookup [dwIndex].Object ; } else { return NULL ; } } } ; template < DWORD dwLookupSize, DWORD dwMax, class T > class TLookupTable : public TLookupTableBase { BYTE m_Filter [dwLookupSize] ; LOOKUP_RECORD m_Lookup [dwMax] ; public : TLookupTable ( ) : TLookupTableBase ( dwLookupSize, dwMax, m_Filter, m_Lookup ) {} } ; // ---------------------------------------------------------------------------- // ObjectPool // // non-serialized object pool // ---------------------------------------------------------------------------- template class ObjectPool { template struct CONTAINER { LIST_ENTRY ListEntry ; DWORD dwIndex ; T Object ; static CONTAINER * RecoverContainer ( IN T * pObject ) { CONTAINER * pContainer ; pContainer = CONTAINING_RECORD (pObject, CONTAINER , Object) ; return pContainer ; } } ; template struct ALLOCATION_UNIT { LIST_ENTRY ListEntry ; DWORD dwInUseCount ; CONTAINER Container [dwAllocationQuantum] ; } ; LIST_ENTRY m_AllocationUnits ; LIST_ENTRY m_FreeList ; LIST_ENTRY m_InUseList ; DWORD m_dwTotalFreeCount ; ALLOCATION_UNIT * GetOwningAllocationUnit_ ( IN CONTAINER * pContainer ) { ALLOCATION_UNIT * pAllocationUnit ; pAllocationUnit = CONTAINING_RECORD (pContainer, ALLOCATION_UNIT , Container [pContainer -> dwIndex]) ; return pAllocationUnit ; } void InitializeAllocationUnit_ ( IN ALLOCATION_UNIT * pNewAllocationUnit ) { DWORD i ; for (i = 0; i < dwAllocationQuantum; i++) { pNewAllocationUnit -> Container [i].dwIndex = i ; InsertHeadList (& m_FreeList, & (pNewAllocationUnit -> Container [i].ListEntry)) ; m_dwTotalFreeCount++ ; } InsertHeadList (& m_AllocationUnits, & (pNewAllocationUnit -> ListEntry)) ; pNewAllocationUnit -> dwInUseCount = 0 ; } void MaybeRecycleAllocationUnit_ ( IN ALLOCATION_UNIT * pAllocationUnit ) { DWORD i ; if (pAllocationUnit -> dwInUseCount == 0 && m_dwTotalFreeCount > dwAllocationQuantum) { for (i = 0; i < dwAllocationQuantum; i++) { FreeListPop_ (& (pAllocationUnit -> Container [i])) ; } RemoveEntryList (& (pAllocationUnit -> ListEntry)) ; delete pAllocationUnit ; } } void FreeListPush_ ( IN CONTAINER * pContainer ) { InsertHeadList (& m_FreeList, & (pContainer -> ListEntry)) ; m_dwTotalFreeCount++ ; } void FreeListPop_ ( IN CONTAINER * pContainer ) { RemoveEntryList (& (pContainer -> ListEntry)) ; m_dwTotalFreeCount-- ; } CONTAINER * FreeListPop_ ( ) { LIST_ENTRY * pListEntry ; CONTAINER * pContainer ; if (IsListEmpty (& m_FreeList) == FALSE) { pListEntry = RemoveHeadList (& m_FreeList) ; m_dwTotalFreeCount-- ; pContainer = CONTAINING_RECORD (pListEntry, CONTAINER , ListEntry) ; return pContainer ; } else { return NULL ; } } void InUseListPop_ ( IN CONTAINER * pContainer ) { RemoveEntryList (& pContainer -> ListEntry) ; } void InUseListPush_ ( IN CONTAINER * pContainer ) { InsertHeadList (& m_InUseList, & (pContainer -> ListEntry)) ; } public : ObjectPool ( ) : m_dwTotalFreeCount (0) { InitializeListHead (& m_AllocationUnits) ; InitializeListHead (& m_FreeList) ; InitializeListHead (& m_InUseList) ; } ~ObjectPool ( ) { ALLOCATION_UNIT * pAllocationUnit ; LIST_ENTRY * pListEntry ; while (IsListEmpty (& m_AllocationUnits) == FALSE) { pListEntry = RemoveHeadList (& m_AllocationUnits) ; pAllocationUnit = CONTAINING_RECORD (pListEntry, ALLOCATION_UNIT , ListEntry) ; delete pAllocationUnit ; } } T * Get ( ) { ALLOCATION_UNIT * pAllocationUnit ; CONTAINER * pContainer ; pContainer = FreeListPop_ () ; if (pContainer == NULL) { pAllocationUnit = new ALLOCATION_UNIT ; if (pAllocationUnit == NULL) { return NULL ; } InitializeAllocationUnit_ (pAllocationUnit) ; ASSERT (IsListEmpty (& m_FreeList) == FALSE) ; pContainer = FreeListPop_ () ; } ASSERT (pContainer) ; GetOwningAllocationUnit_ (pContainer) -> dwInUseCount++ ; InUseListPush_ (pContainer) ; return & (pContainer -> Object) ; } void Recycle ( IN T * pObject ) { CONTAINER * pContainer ; ALLOCATION_UNIT * pAllocationUnit ; pContainer = CONTAINER ::RecoverContainer (pObject) ; InUseListPop_ (pContainer) ; FreeListPush_ (pContainer) ; pAllocationUnit = GetOwningAllocationUnit_ (pContainer) ; pAllocationUnit -> dwInUseCount-- ; MaybeRecycleAllocationUnit_ (pAllocationUnit) ; } } ; #ifdef _DEBUG #define DEBUG_ONLY(op) { op ; } #else #define DEBUG_ONLY(op) #endif // _DEBUG #define OnList(Entry) \ ((Entry)->Flink != NULL && (Entry)->Blink != NULL) #define NotOnList(Entry) \ (OnList(Entry) == FALSE) #define NullOutListEntry(Entry) {\ (Entry)->Flink = (Entry)->Blink = NULL ; \ } // --------------------------------------------------------------------------- // TGenericMap // --------------------------------------------------------------------------- template < class T, // objects to store in this map class K, // hash key type BOOL fDuplicatesAllowed, // TRUE/FALSE if duplicates are allowed int iAllocationUnitCount, // allocation quantum int ulTableSize // table size; must be prime > class TGenericMap /*++ fixed size hash table; very simple --*/ { /*++ 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 1009 1013 --*/ // this structure references 1 entry struct TABLE_ENTRY { K HashKey ; // hash key (hashed to get index) T Object ; // object int iAllocUnitIndex ; // index in the allocation unit LIST_ENTRY ListEntry ; // listentry for in-use or free list LIST_ENTRY HashList ; // list of TABLE_ENTRY with a common hash, or that are empty } ; // this structure references a list of TABLE_ENTRYs, each of which // hashes to the same value struct HASH_BUCKET { LIST_ENTRY HashListHead ; } ; ObjectPool m_TableEntryPool ; HASH_BUCKET m_Table [ulTableSize] ; // table headers LIST_ENTRY m_EntryList ; // table entries in use CRITICAL_SECTION m_crt ; // crit sect for the lock void Lock_ () { EnterCriticalSection (& m_crt) ; } void Unlock_ () { LeaveCriticalSection (& m_crt) ; } BOOL NoDuplicatesAllowed_ () { return fDuplicatesAllowed == FALSE ; } void HashListsPush_ ( IN ULONG ulHashValue, IN TABLE_ENTRY * pTableEntry ) { ASSERT (ulHashValue < ulTableSize) ; InsertHeadList (& (m_Table [ulHashValue].HashListHead), & (pTableEntry -> HashList)) ; InsertTailList (& m_EntryList, & (pTableEntry -> ListEntry)) ; } void HashListsPop_ ( IN TABLE_ENTRY * pTableEntry ) { RemoveEntryList (& (pTableEntry -> HashList)) ; RemoveEntryList (& (pTableEntry -> ListEntry)) ; } TABLE_ENTRY * FindInHashChain_ ( IN K HashKey, // search for IN LIST_ENTRY * pListStartInclusive, // check this one IN LIST_ENTRY * pListStopExclusive // up to, but not including this one ) // if pListStartInclusive == pListStopExclusive, nothing is checked { TABLE_ENTRY * pCurTableEntry ; TABLE_ENTRY * pRetTableEntry ; LIST_ENTRY * pListEntry ; pRetTableEntry = NULL ; for (pListEntry = pListStartInclusive ; pListEntry != pListStopExclusive ; pListEntry = pListEntry -> Flink) { pCurTableEntry = CONTAINING_RECORD (pListEntry, TABLE_ENTRY, HashList) ; if (pCurTableEntry -> HashKey == HashKey) { // found a match pRetTableEntry = pCurTableEntry ; break ; } } return pRetTableEntry ; } TABLE_ENTRY * FindHashkey_ ( IN K HashKey, IN ULONG ulHashValue ) // searches an entire list of the specified hashkey { return FindInHashChain_ ( HashKey, m_Table [ulHashValue].HashListHead.Flink, // start & (m_Table [ulHashValue].HashListHead) // end point ) ; } TABLE_ENTRY * FindObject_ ( IN T Object, IN K HashKey, IN ULONG ulHashValue ) { TABLE_ENTRY * pCurTableEntry ; LIST_ENTRY * pTableHashListHead ; TABLE_ENTRY * pRetTableEntry ; pRetTableEntry = NULL ; pTableHashListHead = & (m_Table [ulHashValue].HashListHead) ; pCurTableEntry = FindInHashChain_ ( HashKey, // look for pTableHashListHead -> Flink, // starting pTableHashListHead // up to ) ; while (pCurTableEntry) { ASSERT (pCurTableEntry -> HashKey == HashKey) ; if (pCurTableEntry -> Object == Object) { pRetTableEntry = pCurTableEntry ; break ; } // find the next with same hashkey pCurTableEntry = FindInHashChain_ ( HashKey, // look for pCurTableEntry -> HashList.Flink, // starting with next pTableHashListHead // up to ) ; } return pRetTableEntry ; } public : TGenericMap ( ) { ULONG i ; InitializeCriticalSection (& m_crt) ; InitializeListHead (& m_EntryList) ; for (i = 0; i < ulTableSize; i++) { InitializeListHead (& (m_Table [i].HashListHead)) ; } } virtual ~TGenericMap ( ) { DeleteCriticalSection (& m_crt) ; } // -------------------------------------------------------------------- // converts hashkey to a well-distributed ULONG value virtual ULONG Value (IN K HashKey) = 0 ; // -------------------------------------------------------------------- ULONG Hash ( IN K HashKey ) { return Value (HashKey) % ulTableSize ; } HRESULT Store ( IN T Object, IN K HashKey ) { ULONG ulHashValue ; TABLE_ENTRY * pTableEntry ; HRESULT hr ; ulHashValue = Hash (HashKey) ; Lock_ () ; if (NoDuplicatesAllowed_ () && FindHashkey_ (HashKey, ulHashValue) != NULL) { // cannot have duplicates & one was found hr = E_FAIL ; } else { pTableEntry = m_TableEntryPool.Get () ; if (pTableEntry) { pTableEntry -> Object = Object ; pTableEntry -> HashKey = HashKey ; HashListsPush_ ( ulHashValue, pTableEntry ) ; // success hr = S_OK ; } else { // failure hr = E_OUTOFMEMORY ; } } Unlock_ () ; return hr ; } HRESULT Find ( IN K HashKey, OUT T * pObject ) { ULONG ulHashValue ; TABLE_ENTRY * pTableEntry ; HRESULT hr ; ASSERT (pObject) ; ulHashValue = Hash (HashKey) ; Lock_ () ; pTableEntry = FindHashkey_ (HashKey, ulHashValue) ; if (pTableEntry) { // found * pObject = pTableEntry -> Object ; hr = S_OK ; } else { // not found hr = E_FAIL ; } Unlock_ () ; return hr ; } HRESULT FindSpecific ( IN T Object, IN K HashKey ) { ULONG ulHashValue ; TABLE_ENTRY * pTableEntry ; HRESULT hr ; ulHashValue = Hash (HashKey) ; Lock_ () ; pTableEntry = FindObject_ (Object, HashKey, ulHashValue) ; if (pTableEntry) { // success hr = S_OK ; } else { // specific object was not found hr = E_FAIL ; } Unlock_ () ; return hr ; } BOOL Exists ( IN K HashKey ) { HRESULT hr ; T tTmp ; hr = Find (HashKey, & tTmp) ; return SUCCEEDED (hr) ; } HRESULT Remove ( IN K HashKey, OUT T * pObject ) { ULONG ulHashValue ; TABLE_ENTRY * pTableEntry ; HRESULT hr ; ASSERT (pObject) ; ulHashValue = Hash (HashKey) ; Lock_ () ; pTableEntry = FindHashkey_ (HashKey, ulHashValue) ; if (pTableEntry) { * pObject = pTableEntry -> Object ; HashListsPop_ (pTableEntry) ; m_TableEntryPool.Recycle (pTableEntry) ; // success hr = S_OK ; } else { // nothing was found with the specified hashkey hr = E_FAIL ; } Unlock_ () ; return hr ; } HRESULT RemoveSpecific ( IN T Object, IN K HashKey ) { ULONG ulHashValue ; TABLE_ENTRY * pTableEntry ; HRESULT hr ; ulHashValue = Hash (HashKey) ; Lock_ () ; pTableEntry = FindObject_ (Object, HashKey, ulHashValue) ; if (pTableEntry) { HashListsPop_ (pTableEntry) ; m_TableEntryPool.Recycle (pTableEntry) ; // success hr = S_OK ; } else { // not found hr = E_FAIL ; } Unlock_ () ; return hr ; } void Clear ( ) { T tTmp ; Lock_ () ; while (SUCCEEDED (RemoveFirst (& tTmp))) ; Unlock_ () ; } BOOL IsEmpty ( ) { return IsListEmpty (& m_EntryList) ; } HRESULT RemoveFirst ( OUT T * pObject ) { HRESULT hr ; TABLE_ENTRY * pTableEntry ; LIST_ENTRY * pListEntry ; Lock_ () ; if (IsListEmpty (& m_EntryList) == FALSE) { pListEntry = m_EntryList.Flink ; pTableEntry = CONTAINING_RECORD (pListEntry, TABLE_ENTRY, ListEntry) ; * pObject = pTableEntry -> Object ; HashListsPop_ (pTableEntry ) ; m_TableEntryPool.Recycle (pTableEntry) ; hr = S_OK ; } else { hr = E_FAIL ; } Unlock_ () ; return hr ; } } ; // --------------------------------------------------------------------------- // TListEntryBufferManager // --------------------------------------------------------------------------- template struct LIST_ENTRY_CONTAINER { LIST_ENTRY ListEntry ; T Payload ; LONG lRef ; } ; template class TListEntryContainerPool /*++ This template provides a simple pool of LIST_ENTRY_CONTAINER structures. The maximum depth of the pool is specified in the dwMaxBufferPool template parameter. The type of value that is held in the returned LIST_ENTRY_CONTAINER structs is specified by the template parameter "class T". When the pool is empty, new structs are allocated from the heap using "new". When the a struct is recycled and the pool is full, its resources are freed via "delete". When a struct is requested, the pool is always checked first. When a struct has been passed out, there are NO references to it in this template class. It must be returned before the class is destroyed in order for the memory not be leaked. There is an outstanding count that is kept. Debug builds will ASSERT when the class is destroyed with outstanding structs. After a struct is recycled, it must not be accessed again by the caller. Its resources may be returned to the system, or the ListEntry member will be used for the struct pool. When a struct has been obtained, the ListEntry field is available for any use that is required, until the struct is recycled. --*/ { LIST_ENTRY m_BufferPoolHead ; // head pointer to buffer pool DWORD m_cCurrentBufferPool ; // depth of buffer pool currently DWORD m_cOutstandingBuffers ; // count of outstanding buffers CRITICAL_SECTION m_crt ; // class lock void Lock_ () { EnterCriticalSection (& m_crt) ; } void Unlock_ () { LeaveCriticalSection (& m_crt) ; } public : TListEntryContainerPool ( ) : m_cCurrentBufferPool (0), m_cOutstandingBuffers (0) { TRACE_CONSTRUCTOR (TEXT ("TListEntryContainerPool")) ; InitializeListHead (& m_BufferPoolHead) ; InitializeCriticalSection (& m_crt) ; } ~TListEntryContainerPool ( ) { LIST_ENTRY * pListEntry ; LIST_ENTRY_CONTAINER * pContainer ; TRACE_DESTRUCTOR (TEXT ("TListEntryContainerPool")) ; // should not be getting destroyed as long as there are outstanding buffers ASSERT (m_cOutstandingBuffers == 0) ; while (m_cCurrentBufferPool > 0) { ASSERT (IsListEmpty (& m_BufferPoolHead) == FALSE) ; pListEntry = RemoveHeadList (& m_BufferPoolHead) ; pContainer = CONTAINING_RECORD (pListEntry, LIST_ENTRY_CONTAINER , ListEntry) ; delete pContainer ; m_cCurrentBufferPool-- ; } ASSERT (IsListEmpty (& m_BufferPoolHead) == TRUE) ; DeleteCriticalSection (& m_crt) ; } HRESULT Get ( OUT LIST_ENTRY_CONTAINER ** ppListEntryContainer ) { LIST_ENTRY * pListEntry ; Lock_ () ; if (m_cCurrentBufferPool > 0) { ASSERT (IsListEmpty (& m_BufferPoolHead) == FALSE) ; // just pull one off the buffer pool pListEntry = RemoveHeadList (& m_BufferPoolHead) ; * ppListEntryContainer = CONTAINING_RECORD (pListEntry, LIST_ENTRY_CONTAINER , ListEntry) ; m_cCurrentBufferPool-- ; } else { // need to allocate a new one * ppListEntryContainer = new LIST_ENTRY_CONTAINER ; } Unlock_ () ; if (* ppListEntryContainer == NULL) { // failure return E_OUTOFMEMORY ; } // 1 more outstanding buffer m_cOutstandingBuffers++ ; // success return S_OK ; } HRESULT Recycle ( IN LIST_ENTRY_CONTAINER * pListEntryContainer ) { Lock_ () ; // decide if we're going to free the resource or just recycle to the // buffer pool if (m_cCurrentBufferPool < dwMaxBufferPool) { // we have room in the buffer pool InsertHeadList (& m_BufferPoolHead, & pListEntryContainer -> ListEntry) ; m_cCurrentBufferPool++ ; } else { // free the resource; our buffer pool is full delete pListEntryContainer ; } m_cOutstandingBuffers-- ; Unlock_ () ; return S_OK ; } } ; // --------------------------------------------------------------------------- // generic unsorted dense vector // --------------------------------------------------------------------------- template class TSimpleVector /*++ simple dense vector template; allocates from a specified heap or the process heap (default); grows and shrinks (reallocates) as required, as values are added and deleted; does not sort in any manner; can add member randomly; appending is most efficient can remove members randomly; deleted from end is most efficient --*/ { // default values enum { DEF_INITIAL_SIZE = 4, // initialize size DEF_RESIZE_MULTIPLIER = 2 // multiplier/divisor of size } ; T * m_pVector ; // vector of objects DWORD m_cElements ; // number of elements; <= m_dwMaxSize DWORD m_dwResizeMultiplier ; // resize/shrink multiplier/divisor DWORD m_dwInitialSize ; // first allocation HANDLE m_hHeap ; // heap handle (not duplicated !) DWORD m_dwMaxSize ; // current max size of vector CRITICAL_SECTION m_crt ; // used in the Lock/Unlock methods void MaybeGrow_ ( ) /*++ --*/ { T * pNewVector ; DWORD dwNewSize ; ASSERT (m_hHeap) ; // if we're maxed out, grow the vector if (m_cElements == m_dwMaxSize) { if (m_pVector) { // we've been called before, time to realloc ASSERT (m_dwMaxSize > 0) ; dwNewSize = m_dwResizeMultiplier * m_dwMaxSize ; pNewVector = reinterpret_cast (HeapReAlloc ( m_hHeap, HEAP_GENERATE_EXCEPTIONS, m_pVector, dwNewSize * sizeof T )) ; } else { // this is the first time we are allocating dwNewSize = m_dwInitialSize ; pNewVector = reinterpret_cast (HeapAlloc ( m_hHeap, HEAP_GENERATE_EXCEPTIONS, dwNewSize * sizeof T )) ; } // specifying HEAP_GENERATE_EXCEPTIONS in the above calls ensures // that the system will have raised an exception if there's // insufficient memory ASSERT (pNewVector) ; // reset array pointer and set the max size m_pVector = pNewVector ; m_dwMaxSize = dwNewSize ; } return ; } void MaybeShrink_ ( ) { T * pNewVector ; DWORD dwNewSize ; ASSERT (m_pVector) ; ASSERT (m_dwMaxSize > 0) ; ASSERT (m_dwResizeMultiplier > 0) ; ASSERT (m_hHeap) ; // if we fall below the threshold, but are bigger than smallest size, // try to realloc if (m_cElements >= m_dwInitialSize && m_cElements <= (m_dwMaxSize / m_dwResizeMultiplier)) { dwNewSize = m_dwMaxSize / m_dwResizeMultiplier ; pNewVector = reinterpret_cast (HeapReAlloc ( m_hHeap, HEAP_GENERATE_EXCEPTIONS, m_pVector, dwNewSize * sizeof T )) ; // specifying HEAP_GENERATE_EXCEPTIONS in the above calls ensures // that the system will have raised an exception if there's // insufficient memory ASSERT (pNewVector) ; // reset array pointer and set the max size m_dwMaxSize = dwNewSize ; m_pVector = pNewVector ; } return ; } public : TSimpleVector ( IN HANDLE hHeap = NULL, // could specify the heap handle IN DWORD dwInitialSize = DEF_INITIAL_SIZE, // initial entries allocation IN DWORD dwResizeMultiplier = DEF_RESIZE_MULTIPLIER // resize multiplier/divisor ) : m_pVector (NULL), m_dwResizeMultiplier (dwResizeMultiplier), m_dwInitialSize (dwInitialSize), m_dwMaxSize (0), m_cElements (0), m_hHeap (hHeap) { // if no heap was specified, grab the process' heap handle if (m_hHeap == NULL) { m_hHeap = GetProcessHeap () ; } InitializeCriticalSection (& m_crt) ; ASSERT (m_hHeap) ; ASSERT (dwResizeMultiplier > 0) ; ASSERT (dwInitialSize > 0) ; } ~TSimpleVector ( ) { // there's nothing in the docs that says m_pVector can be NULL // when calling HeapFree.. if (m_pVector) { ASSERT (m_hHeap != NULL) ; HeapFree ( m_hHeap, NULL, m_pVector ) ; } DeleteCriticalSection (& m_crt) ; } _inline const DWORD GetCount ( ) // returns the number of elements currently in the vector { return m_cElements ; } __inline T * const GetVector ( ) // returns the vector itself; caller then has direct access to the // memory { return m_pVector ; } __inline void GetVector ( OUT T ** ppT, OUT DWORD * pcElements ) { ASSERT (ppT) ; ASSERT (pcElements) ; * ppT = m_pVector ; * pcElements = m_cElements ; } T operator [] ( IN DWORD dwIndex ) { T t ; HRESULT hr ; hr = Get (dwIndex, & t) ; if (SUCCEEDED (hr)) { return t ; } else { return 0 ; } } __inline void Lock ( ) { EnterCriticalSection (& m_crt) ; } __inline void Unlock ( ) { LeaveCriticalSection (& m_crt) ; } __inline HRESULT const Get ( IN DWORD dwIndex, OUT T * pValue ) // returns the dwIndex'th element in the vector { ASSERT (pValue) ; if (dwIndex >= m_cElements) { return E_INVALIDARG ; } * pValue = m_pVector [dwIndex] ; return S_OK ; } HRESULT Append ( IN T Value, OUT DWORD * pcTotalElements = NULL ) /*++ Purpose: Appends a new value to the end of the vector. Optionally returns the number of elements in the vector. Parameters: Value new item pcTotalElements optional OUT parameter to return the number of elements in the vector AFTER the call; valid only if the call is successfull Return Values: S_OK success E_OUTOFMEMORY the vector is maxed and the memory reallocation failed --*/ { return Insert (m_cElements, Value, pcTotalElements) ; } HRESULT Insert ( IN DWORD dwIndex, IN T Value, OUT DWORD * pcTotalElements = NULL ) /*++ Purpose: Insert a new value at the specified Index Parameters: dwIndex 0-based index to the position where the new item should be inserted Value new item pcTotalElements optional OUT parameter to return the number of elements in the vector after the call; valid only if successfull Return Values: S_OK success E_OUTOFMEMORY the vector is maxed and the memory reallocation failed E_INVALIDARG the specified index is out of range of the current contents of the vector; the min valid value is 0, and the max valid value is after the last elemtn --*/ { // make sure we're not going to insert off the end of the vector; // m_cElements is the max valid index for a new element (in which // case we are appending) if (dwIndex > m_cElements) { return E_INVALIDARG ; } // if we didn't get this when we instantiated, we try again if (m_hHeap == NULL) { m_hHeap = GetProcessHeap () ; if (m_hHeap == NULL) { return E_OUTOFMEMORY ; } } // frame this in a try-except block to catch out-of-memory // exceptions __try { MaybeGrow_ () ; } __except (GetExceptionCode () == STATUS_NO_MEMORY || GetExceptionCode () == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { return E_OUTOFMEMORY ; } // the only failure to MaybeGrow_ is a win32 exception, so if we // get to here, we've got the memory we need ASSERT (m_cElements < m_dwMaxSize) ; ASSERT (m_pVector) ; // if there are elements to move, and we're not just appending, move // the remaining elements out to make room if (m_cElements > 0 && dwIndex < m_cElements) { // expand MoveMemory ( & m_pVector [dwIndex + 1], & m_pVector [dwIndex], (m_cElements - dwIndex) * sizeof T ) ; } // insert the new item m_pVector [dwIndex] = Value ; m_cElements++ ; // if the caller wants to know size, set that now if (pcTotalElements) { * pcTotalElements = m_cElements ; } return S_OK ; } HRESULT Remove ( IN DWORD dwIndex, OUT T * pValue = NULL ) /*++ Purpose: Removes an item at the specified 0-based index. Optionally returns the value in the [out] parameter. Parameters: dwIndex 0-based index pValue optional pointer to a value to obtain what was removed Return Values: S_OK success E_INVALIDARG an out-of-range index was specified --*/ { ASSERT (m_hHeap != NULL) ; // dwIndex is 0-based if (dwIndex >= m_cElements) { return E_INVALIDARG ; } // if caller wants to get the Remove'd value, set it now if (pValue) { * pValue = m_pVector [dwIndex] ; } // compact the remaining elements, unless we're removing the last element // check above ensures that subtracting 1 does not wrap if (dwIndex < m_cElements - 1) { // compact MoveMemory ( & m_pVector [dwIndex], & m_pVector [dwIndex + 1], (m_cElements - 1 - dwIndex) * sizeof T ) ; } m_cElements-- ; __try { MaybeShrink_ () ; } __except (GetExceptionCode () == STATUS_NO_MEMORY || GetExceptionCode () == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { // fail silently; we still have the memory we had before } return S_OK ; } } ; // ---------------------------------------------------------------------------- // CTDynArray // ---------------------------------------------------------------------------- template class CTDynArray { // FIFO: insert at "tail"; remove at "head" // LIFO: insert at "top" remove at "top" enum { INIT_TABLE_SIZE = 5 } ; T ** m_ppBlockTable ; LONG m_lAllocatedTableSize ; LONG m_lAllocatedBlocks ; LONG m_lAllocQuantum ; LONG m_lMaxArrayLen ; LONG m_lCurArrayLen ; LONG m_lLastInsertSlotIndex ; LONG AllocatedArrayLength_ () { return m_lAllocatedBlocks * m_lAllocQuantum ; } LONG TableIndex_ (IN LONG lArrayIndex) { return lArrayIndex / m_lAllocQuantum ; } LONG BlockIndex_ (IN LONG lArrayIndex) { return lArrayIndex % m_lAllocQuantum ; } LONG NextInsertSlotIndex_ () { return (AllocatedArrayLength_ () > 0 ? (m_lLastInsertSlotIndex + m_lCurArrayLen) % AllocatedArrayLength_ () : 0) ; } LONG FIFO_HeadIndex_ () { return m_lLastInsertSlotIndex ; } void FIFO_PopHead_ () { ASSERT (m_lCurArrayLen > 0) ; m_lCurArrayLen-- ; m_lLastInsertSlotIndex = (m_lLastInsertSlotIndex + 1) % AllocatedArrayLength_ () ; } LONG LIFO_TopIndex_ () { ASSERT (m_lCurArrayLen > 0) ; return (m_lLastInsertSlotIndex + m_lCurArrayLen - 1) % AllocatedArrayLength_ () ; } void LIFO_PopTop_ () { ASSERT (m_lCurArrayLen > 0) ; m_lCurArrayLen-- ; } DWORD MaybeGrowTable_ ( ) { LONG lNewSize ; T ** pptNew ; if (m_lAllocatedTableSize == m_lAllocatedBlocks) { // need to allocate more table lNewSize = (m_lAllocatedTableSize > 0 ? m_lAllocatedTableSize * 2 : INIT_TABLE_SIZE) ; pptNew = AllocateTable_ (lNewSize) ; if (!pptNew) { return ERROR_NOT_ENOUGH_MEMORY ; } ZeroMemory (pptNew, lNewSize * sizeof (T *)) ; if (m_ppBlockTable) { ASSERT (m_lAllocatedTableSize > 0) ; CopyMemory ( pptNew, m_ppBlockTable, m_lAllocatedTableSize * sizeof (T *) ) ; FreeTable_ (m_ppBlockTable) ; } m_ppBlockTable = pptNew ; m_lAllocatedTableSize = lNewSize ; } return NOERROR ; } T * Entry_ ( IN LONG lIndex ) { // should not be asking if it's beyond ASSERT (lIndex < AllocatedArrayLength_ ()) ; ASSERT (TableIndex_ (lIndex) < m_lAllocatedTableSize) ; ASSERT (m_ppBlockTable [TableIndex_ (lIndex)]) ; return & m_ppBlockTable [TableIndex_ (lIndex)] [BlockIndex_ (lIndex)] ; } T Val_ (IN LONG lIndex) { return (* Entry_ (lIndex)) ; } DWORD Append_ (T tVal) { T * ptNewBlock ; LONG lNewBlockTableIndex ; DWORD dw ; ASSERT (!ArrayMaxed ()) ; if ((NextInsertSlotIndex_ () == m_lLastInsertSlotIndex && m_lCurArrayLen > 0) || AllocatedArrayLength_ () == 0) { // table is full; need to allocate // might need to extend the table to hold more blocks dw = MaybeGrowTable_ () ; if (dw != NOERROR) { return dw ; } ASSERT (m_lAllocatedTableSize > m_lAllocatedBlocks) ; // allocate our new block ptNewBlock = AllocateBlock_ () ; if (!ptNewBlock) { return ERROR_NOT_ENOUGH_MEMORY ; } // new block is inserted in table into OutSlotIndex's block; we'll // then move out the OutSlotIndex lNewBlockTableIndex = TableIndex_ (m_lLastInsertSlotIndex) ; // init the new block & make room for the new block (if there's // something there now i.e. this is not our first time through) if (m_ppBlockTable [lNewBlockTableIndex]) { // copy the contents of the block we're about to move out CopyMemory ( ptNewBlock, m_ppBlockTable [lNewBlockTableIndex], m_lAllocQuantum * sizeof T ) ; // shift the blocks that follow out MoveMemory ( & m_ppBlockTable [lNewBlockTableIndex + 1], & m_ppBlockTable [lNewBlockTableIndex], (m_lAllocatedBlocks - lNewBlockTableIndex) * sizeof (T *) ) ; } // insert into the table m_ppBlockTable [lNewBlockTableIndex] = ptNewBlock ; m_lAllocatedBlocks++ ; // shift the OutSlot out, if this is not our first m_lLastInsertSlotIndex += (m_lAllocatedBlocks > 1 ? m_lAllocQuantum : 0) ; } // append to tail (* Entry_ (NextInsertSlotIndex_ ())) = tVal ; m_lCurArrayLen++ ; return NOERROR ; } protected : T * AllocateBlock_ ( ) { return reinterpret_cast (malloc (m_lAllocQuantum * sizeof T)) ; } T ** AllocateTable_ ( IN int iNumEntries ) { return reinterpret_cast (malloc (iNumEntries * sizeof (T *))) ; } void FreeBlock_ ( IN T * pBlock ) { free (pBlock) ; } void FreeTable_ ( IN T ** pptTable ) { free (pptTable) ; } DWORD FIFOHeadVal_ (OUT T * pt) { DWORD dw ; if (m_lCurArrayLen > 0) { (* pt) = Val_ (FIFO_HeadIndex_ ()) ; dw = NOERROR ; } else { dw = ERROR_GEN_FAILURE ; } return dw ; } DWORD PopFIFO_ (OUT T * pt) { DWORD dw ; dw = FIFOHeadVal_ (pt) ; if (dw == NOERROR) { FIFO_PopHead_ () ; } return dw ; } DWORD LIFOTopVal_ (OUT T * pt) { DWORD dw ; if (m_lCurArrayLen > 0) { (* pt) = Val_ (LIFO_TopIndex_ ()) ; dw = NOERROR ; } else { dw = ERROR_GEN_FAILURE ; } return dw ; } DWORD PopLIFO_ (OUT T * pt) { DWORD dw ; dw = LIFOTopVal_ (pt) ; if (dw == NOERROR) { LIFO_PopTop_ () ; } return dw ; } public : CTDynArray ( IN LONG lAllocQuantum, IN LONG lMaxArrayLen = LONG_MAX ) : m_lAllocQuantum (lAllocQuantum), m_lAllocatedBlocks (0), m_lAllocatedTableSize (0), m_ppBlockTable (NULL), m_lMaxArrayLen (lMaxArrayLen), m_lCurArrayLen (0), m_lLastInsertSlotIndex (0) {} ~CTDynArray ( ) { LONG l ; for (l = 0; l < m_lAllocatedBlocks; l++) { FreeBlock_ (m_ppBlockTable [l]) ; } FreeTable_ (m_ppBlockTable) ; } LONG Length () { return m_lCurArrayLen ; } BOOL ArrayMaxed () { return (m_lCurArrayLen < m_lMaxArrayLen ? FALSE : TRUE) ; } BOOL Empty () { return (Length () > 0 ? FALSE : TRUE) ; } void Reset () { m_lCurArrayLen = 0 ; } DWORD Push (IN T tVal) { DWORD dw ; if (!ArrayMaxed ()) { dw = Append_ (tVal) ; } else { dw = ERROR_GEN_FAILURE ; } return dw ; } virtual DWORD Pop ( OUT T * pt ) = 0 ; } ; // ---------------------------------------------------------------------------- // CTDynQueue // ---------------------------------------------------------------------------- template class CTDynQueue : public CTDynArray { public : CTDynQueue ( IN LONG lAllocQuantum, IN LONG lMaxQueueLen = LONG_MAX ) : CTDynArray (lAllocQuantum, lMaxQueueLen) {} virtual DWORD Pop (OUT T * pt) { return PopFIFO_ (pt) ; } DWORD Head (OUT T * pt) { return FIFOHeadVal_ (pt) ; } DWORD Tail (OUT T * pt) { return LIFOTopVal_ (pt) ; } } ; // ---------------------------------------------------------------------------- // CTDynStack // ---------------------------------------------------------------------------- template class CTDynStack : public CTDynArray { public : CTDynStack ( IN LONG lAllocQuantum, IN LONG lMaxQueueLen = LONG_MAX ) : CTDynArray (lAllocQuantum, lMaxQueueLen) {} virtual DWORD Pop (OUT T * pt) { return PopLIFO_ (pt) ; } DWORD Top (OUT T * pt) { return LIFOTopVal_ (pt) ; } } ; // --------------------------------------------------------------------------- // CTMovingAverage // --------------------------------------------------------------------------- template class CTMovingAverage { CTDynQueue m_EntryQueue ; T m_Total ; LONG m_lMaxWindowSize ; CCritSec m_cs ; T m_tCurAvg ; public : CTMovingAverage ( IN LONG lMaxWindowSize ) : m_EntryQueue (lMaxWindowSize, lMaxWindowSize), m_lMaxWindowSize (lMaxWindowSize), m_Total (0) {} void Reset () { m_EntryQueue.Empty () ; m_Total = 0 ; m_tCurAvg = 0 ; } DWORD Entries () { return m_EntryQueue.Length () ; } T Average () { return m_tCurAvg ; } DWORD NewEntry ( IN T Val ) { T tHead ; CAutoLock Lock (& m_cs) ; DWORD dwRet ; if (m_EntryQueue.Length () == m_lMaxWindowSize) { m_EntryQueue.Pop (& tHead) ; m_Total -= tHead ; } dwRet = m_EntryQueue.Push (Val) ; if (dwRet == NOERROR) { m_Total += Val ; ASSERT (m_EntryQueue.Length ()) ; m_tCurAvg = m_Total / m_EntryQueue.Length () ; } return dwRet ; } } ; typedef int (__cdecl * PFN_QSORT_COMPARE) (const void *, const void *) ; // binary search of a vector; returns an index template HRESULT BSearch ( IN TSimpleVector * pVector, IN T Search, IN PFN_QSORT_COMPARE pfnCompare, OUT DWORD * pdwIndex ) { T * p ; DWORD c ; int index, start, end, r ; pVector -> GetVector (& p, & c) ; if (p) { ASSERT (c > 0) ; start = 0 ; end = c - 1 ; while (start <= end) { index = (start + end) / 2 ; // compare r = pfnCompare ( & p [index], & Search ) ; // found it if (r == 0) { if (pdwIndex) { * pdwIndex = index ; } return S_OK ; } // search bracket moves up else if (r < 0) { start = index + 1 ; } // search bracket moves down else { end = index - 1 ; } } } return E_FAIL ; } // binary search of a vector; returns index of first element, and how many // there are in the bracket template HRESULT BSearchBracket ( IN TSimpleVector * pVector, // vector to search in IN T Search, // what we're looking for IN PFN_QSORT_COMPARE pfnCompare, // comparison function OUT DWORD * pdwFirstIndex, // index of first in bracket OUT DWORD * pcInBracket // number of elements in the bracket ) { T * p ; DWORD cElements ; int index, start, end, r ; ASSERT (pdwFirstIndex) ; ASSERT (pcInBracket) ; pVector -> GetVector (& p, & cElements) ; if (p) { ASSERT (cElements > 0) ; start = 0 ; end = cElements - 1 ; while (start <= end) { index = (start + end) / 2 ; // compare r = pfnCompare ( & p [index], & Search ) ; // found it if (r == 0) { ASSERT (pcInBracket) ; // we now must discover the first and then count the number // PID maps for this particular pin // backup for (; index > 0 && pfnCompare (& p [index], & Search) == 0; index--) ; // out of the failure condition if (index > 0) { // if we didn't stop because we were at the beginning of the vector, // increment the vector back to where the recorcs are equal ASSERT (pfnCompare (& p [index], & Search) != 0) ; index++ ; } else { // we're at the beginning of the vector; might be equal, or might // not; check here and increment the index back into the equal range // if they are equal if (pfnCompare (& p [index], & Search) != 0) { index++ ; } } // should now have the index to the first ASSERT (pfnCompare (& p [index], & Search) == 0) ; // now seek up until we are out of the equal range or to the end of the vector for (* pcInBracket = 0;;) { // if we found another record if (pfnCompare (& p [index + (* pcInBracket)], & Search) == 0) { // increment our counter (* pcInBracket)++ ; } else { // otherwise break out break ; } // if we're to the end of the vector, break out if (index + (* pcInBracket) == cElements) { break ; } } * pdwFirstIndex = index ; return S_OK ; } // search bracket moves up else if (r < 0) { start = index + 1 ; } // search bracket moves down else { end = index - 1 ; } } } return E_FAIL ; } // external to internal HRESULT TransportMediaSampleContentEtoI ( IN DWORD ExtMediaSampleContent, // transport OUT DWORD * pdwIntMediaSampleContent ) ; HRESULT ProgramMediaSampleContentEtoI ( IN DWORD ExtMediaSampleContent, // program OUT DWORD * pdwIntMediaSampleContent ) ; // internal to external HRESULT TransportMediaSampleContentItoE ( IN DWORD dwIntMediaSampleContent, OUT DWORD * pExtMediaSampleContent // transport ) ; HRESULT ProgramMediaSampleContentItoE ( IN DWORD dwIntMediaSampleContent, OUT DWORD * pExtMediaSampleContent // program ) ; BOOL IsVideoMediaType ( IN AM_MEDIA_TYPE * pmt ) ; BOOL IsAudioMediaType ( IN AM_MEDIA_TYPE * pmt ) ; BOOL IsAVMediaType ( IN AM_MEDIA_TYPE * pmt ) ; int ParserArgSize ( IN DWORD dwMediaSampleContent ) ; __inline BYTE StartCode ( IN BYTE * pbStartCode ) { return pbStartCode [3] ; } __inline BOOL IsStartCodePrefix ( IN BYTE * pbBuffer ) { return (pbBuffer [0] == 0x00 && pbBuffer [1] == 0x00 && pbBuffer [2] == 0x01) ; } // TRUE : (* ppbBuffer) points to prefix i.e. = {00,00,01} // FALSE: (* piBufferLength) == 2 __inline BOOL SeekToPrefix ( IN OUT BYTE ** ppbBuffer, IN OUT int * piBufferLength ) { while ((* piBufferLength) >= START_CODE_PREFIX_LENGTH) { if (IsStartCodePrefix (* ppbBuffer)) { return TRUE ; } // advance to next byte and decrement number of bytes left (* piBufferLength)-- ; (* ppbBuffer)++ ; } return FALSE ; } // searches BACKWARDS through the past buffer // TRUE : (* ppbBuffer) points to prefix i.e. = {00,00,01} // FALSE: (* piBufferLength) == 2 __inline BOOL SeekBackFromEndForPrefix ( IN OUT BYTE ** ppbBuffer, // points to start of buffer IN OUT int * piBufferLength // length of buffer ) { BYTE * pb ; if ((* piBufferLength) >= START_CODE_PREFIX_LENGTH) { // must give ourselves room at the end (* piBufferLength) -= START_CODE_PREFIX_LENGTH ; // set pbEnd to the last possible place we can determine if there's // a prefix pb = (* ppbBuffer) + (* piBufferLength) ; while ((* piBufferLength) >= 0) { if (IsStartCodePrefix (pb)) { // found one; set the outgoing pointer (* ppbBuffer) = pb ; return TRUE ; } // advance to previous byte and decrement number of bytes left (* piBufferLength)-- ; pb-- ; } } return FALSE ; } static __inline LONGLONG PCRToPTS ( IN LONGLONG llPCR ) { return llPCR / 300 ; } static __inline LONGLONG PTSToPCR ( IN LONGLONG llPCR ) { return llPCR * 300 ; } __inline ULONGLONG PCRMinusPCR ( IN ULONGLONG ullPCRLater, IN ULONGLONG ullPCREarlier ) // returns ullVal1 - ullVal2, taking into consideration that PCRs are 48 bit values, and // that we are storing them in 64 bit types; don't include top 16 bits if there's an overflow { return ullPCRLater - ullPCREarlier - (ullPCRLater < ullPCREarlier ? 0xffff000000000000 : 0) ; } __inline DWORD QPCToMillis ( IN LONGLONG llQPC, IN DWORD dwQPCFreq ) { return (DWORD) ((1000 * llQPC) / dwQPCFreq) ; } __inline REFERENCE_TIME PCRToDShow ( IN LONGLONG llPCR ) { // safe to use since the top 16 bits are never set ASSERT ((llPCR & 0xffff000000000000) == 0x0000000000000000) ; return (REFERENCE_TIME) llMulDiv (llPCR, 10, 27, 0) ; } __inline REFERENCE_TIME SCRToDShow ( IN LONGLONG llSCR ) { return PCRToDShow (llSCR) ; } __inline REFERENCE_TIME QPCToDShow ( IN LONGLONG llQPC, IN LONGLONG llQPCFreq ) { double dRetval ; // BUGBUG: this could wrap dRetval = llQPC * (10000000.0 / ((double) llQPCFreq)) ; return (REFERENCE_TIME) dRetval ; } __inline REFERENCE_TIME PTSToDShow ( IN ULONGLONG ullPTS ) { ASSERT ((ullPTS & 0xffff000000000000) == 0x0000000000000000) ; return (REFERENCE_TIME) llMulDiv (ullPTS, 1000, 9, 0) ; } __inline ULONGLONG DShowToPTS ( IN REFERENCE_TIME rtPTS ) { return (ULONGLONG) ::llMulDiv (rtPTS, 9, 1000, 0) ; } __inline DWORD DShowToMillis ( IN REFERENCE_TIME rt ) { return (DWORD) (rt / (DSHOW_TICKS_PER_SECOND / MILLISECONDS_PER_SECOND)) ; } __inline REFERENCE_TIME MillisToDShow ( IN DWORD dwMillis ) { return static_cast (dwMillis * (DSHOW_TICKS_PER_SECOND / MILLISECONDS_PER_SECOND)) ; } // shameless stolen from DVDNAV class CAnalogCopyProtection { public: CAnalogCopyProtection(IBaseFilter *pFilter) ; ~CAnalogCopyProtection(void) ; BOOL IsAnyVRSuspected(void) { return m_bVRNoKsPS ; } ; BOOL IsACPFailIgnored(void) { return m_bVerNT5 && !m_bDateExpired; } ; HRESULT MakeACPFilterList(void) ; HRESULT FreeACPFilterList(void) ; HRESULT DistributeAnalogCopyProtection(DWORD dwCPBits) ; void ClearAnalogCopyProtectionLevel(void) { m_dwLastCPBits = 0 ; } ; BYTE GetACPLevel(void) { return (BYTE) (m_dwLastCPBits & 0xFF) ; } private: CGenericList m_KsPSList ; // List of filters for analog copy protection BOOL m_bVRNoKsPS ; // Any Video Renderer w/o IKsPropertySet interface? DWORD m_dwLastCPBits ; // Last analog copy protection value distributed BOOL m_bDateExpired ; // Has the date expired for this component? BOOL m_bVerNT5 ; // Is running under NT5.x? IBaseFilter *m_pFilter ; // containing filter pointer } ; /*++ smooths from 0 (after reset) to the target value; as the target value is updated, will tend towards it, but within the constraints of a max step value (per sec) specified; when reset, starts again at 0; target value can be updated anytime, but should be 0-based --*/ template class CTSmoothingFilter { enum { DEF_BUCKET_MILLIS = 10 } ; // not serialized class CTimeBracket { DWORD m_dwStartMillis ; const DWORD m_dwMaxAllowableElapsed ; public : CTimeBracket ( IN DWORD dwMaxElapsed ) : m_dwStartMillis (0), m_dwMaxAllowableElapsed (dwMaxElapsed) {} void Restart () { m_dwStartMillis = ::timeGetTime () ; } DWORD ElapsedMillis () { return Min (m_dwMaxAllowableElapsed, ::timeGetTime () - m_dwStartMillis) ; } } ; T m_tTargetVal ; T m_tCurVal ; CRITICAL_SECTION m_crt ; T m_tMaxStepPerMilli ; CTimeBracket m_TimeBracket ; T m_tCurAllowableError ; double m_dAllowableErrorDegradation ; void Lock_ () { ::EnterCriticalSection (& m_crt) ; } void Unlock_ () { ::LeaveCriticalSection (& m_crt) ; } public : CTSmoothingFilter ( IN T tMaxStepPerMilli, IN double dAllowableErrorDegradation, IN DWORD dwBucketMillis = DEF_BUCKET_MILLIS ) : m_tMaxStepPerMilli (tMaxStepPerMilli), m_dAllowableErrorDegradation (dAllowableErrorDegradation), m_TimeBracket (dwBucketMillis) { ::InitializeCriticalSection (& m_crt) ; Reset () ; ASSERT (tMaxStepPerMilli != 0) ; } ~CTSmoothingFilter ( ) { ::DeleteCriticalSection (& m_crt) ; } void Reset ( IN T tStartVal = 0 ) { Lock_ () ; m_tTargetVal = tStartVal ; m_tCurVal = tStartVal ; m_tCurAllowableError = 0 ; m_TimeBracket.Restart () ; Unlock_ () ; } T TargetVal () { return m_tTargetVal ; } T CurVal () { return m_tCurVal ; } T AdjustCurVal ( ) { T tCurVal ; T tCurDelta ; T tStep ; DWORD dwElapsedMillis ; Lock_ () ; if (m_tCurVal != m_tTargetVal) { if (m_tTargetVal > m_tCurVal + m_tCurAllowableError) { // ---------------------------------------------------------------- // adjust up dwElapsedMillis = m_TimeBracket.ElapsedMillis () ; if (dwElapsedMillis > 0) { // delta tCurDelta = Abs (m_tTargetVal - m_tCurVal) ; // step tStep = Min (tCurDelta, m_tMaxStepPerMilli * dwElapsedMillis) ; // adjust m_tCurVal += tStep ; //TRACE_4 (LOG_TIMING, 1, TEXT ("UP (elapsed = %u) CurDelta = %I64d; Step = %I64d; curval = %I64d ms"), dwElapsedMillis, tCurDelta, tStep, m_tCurVal/10000) ; // set our bracket m_tCurAllowableError = Min (tCurDelta, m_tCurAllowableError) ; // start our timer again m_TimeBracket.Restart () ; } } else if (m_tTargetVal < m_tCurVal - m_tCurAllowableError) { // ---------------------------------------------------------------- // adjust down dwElapsedMillis = m_TimeBracket.ElapsedMillis () ; if (dwElapsedMillis > 0) { // delta tCurDelta = Abs (m_tCurVal - m_tTargetVal) ; // step tStep = Min (tCurDelta, m_tMaxStepPerMilli * dwElapsedMillis) ; //TRACE_4 (LOG_TIMING, 1, TEXT ("DOWN (elapsed = %u) CurDelta = %I64d; Step = %I64d; curval = %I64d ms"), dwElapsedMillis, tCurDelta, tStep, m_tCurVal/10000) ; // adjust m_tCurVal -= tStep ; // set our bracket m_tCurAllowableError = Min (tCurDelta, m_tCurAllowableError) ; // start our timer again m_TimeBracket.Restart () ; } } else { // ---------------------------------------------------------------- // degrade our allowable error bracket m_tCurAllowableError = (T) (m_dAllowableErrorDegradation * (double) m_tCurAllowableError) ; //TRACE_1 (LOG_TIMING, 1, TEXT ("DEGRADE m_tCurAllowableError = %I64d"), m_tCurAllowableError) ; } } tCurVal = m_tCurVal ; Unlock_ () ; return tCurVal ; } void SetTargetVal ( IN T tVal ) { Lock_ () ; m_tTargetVal = tVal ; Unlock_ () ; } } ; #endif // __mp2demux_demxutil_h