1
//------------------------------------------------------------------------------
4
// Desc: DirectShow base classes - implements a non-MFC based generic list
6
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
7
//------------------------------------------------------------------------------
9
#include <pjmedia-videodev/config.h>
11
#if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
13
/* A generic list of pointers to objects.
14
Objectives: avoid using MFC libraries in ndm kernel mode and
15
provide a really useful list type.
17
The class is thread safe in that separate threads may add and
18
delete items in the list concurrently although the application
19
must ensure that constructor and destructor access is suitably
22
The list name must not conflict with MFC classes as an
23
application may use both
25
The nodes form a doubly linked, NULL terminated chain with an anchor
26
block (the list object per se) holding pointers to the first and last
27
nodes and a count of the nodes.
28
There is a node cache to reduce the allocation and freeing overhead.
29
It optionally (determined at construction time) has an Event which is
30
set whenever the list becomes non-empty and reset whenever it becomes
32
It optionally (determined at construction time) has a Critical Section
33
which is entered during the important part of each operation. (About
34
all you can do outside it is some parameter checking).
36
The node cache is a repository of nodes that are NOT in the list to speed
37
up storage allocation. Each list has its own cache to reduce locking and
38
serialising. The list accesses are serialised anyway for a given list - a
39
common cache would mean that we would have to separately serialise access
40
of all lists within the cache. Because the cache only stores nodes that are
41
not in the list, releasing the cache does not release any list nodes. This
42
means that list nodes can be copied or rechained from one list to another
43
without danger of creating a dangling reference if the original cache goes
46
Questionable design decisions:
47
1. Retaining the warts for compatibility
48
2. Keeping an element count -i.e. counting whenever we do anything
49
instead of only when we want the count.
50
3. Making the chain pointers NULL terminated. If the list object
51
itself looks just like a node and the list is kept as a ring then
52
it reduces the number of special cases. All inserts look the same.
58
/* set cursor to the position of each element of list in turn */
59
#define INTERNALTRAVERSELIST(list, cursor) \
60
for ( cursor = (list).GetHeadPositionI() \
62
; cursor = (list).Next(cursor) \
66
/* set cursor to the position of each element of list in turn
69
#define INTERNALREVERSETRAVERSELIST(list, cursor) \
70
for ( cursor = (list).GetTailPositionI() \
72
; cursor = (list).Prev(cursor) \
75
/* Constructor calls a separate initialisation function that
76
creates a node cache, optionally creates a lock object
77
and optionally creates a signaling object.
79
By default we create a locking object, a DEFAULTCACHE sized
80
cache but no event object so the list cannot be used in calls
81
to WaitForSingleObject
83
CBaseList::CBaseList(__in_opt LPCTSTR pName, // Descriptive list name
84
INT iItems) : // Node cache size
95
CBaseList::CBaseList(__in_opt LPCTSTR pName) : // Descriptive list name
102
m_Cache(DEFAULTCACHE)
107
CBaseList::CBaseList(__in_opt LPCSTR pName, // Descriptive list name
108
INT iItems) : // Node cache size
119
CBaseList::CBaseList(__in_opt LPCSTR pName) : // Descriptive list name
126
m_Cache(DEFAULTCACHE)
132
/* The destructor enumerates all the node objects in the list and
133
in the cache deleting each in turn. We do not do any processing
134
on the objects that the list holds (i.e. points to) so if they
135
represent interfaces for example the creator of the list should
136
ensure that each of them is released before deleting us
138
CBaseList::~CBaseList()
140
/* Delete all our list nodes */
146
/* Remove all the nodes from the list but don't do anything
147
with the objects that each node looks after (this is the
148
responsibility of the creator).
149
Aa a last act we reset the signalling event
150
(if available) to indicate to clients that the list
151
does not have any entries in it.
153
void CBaseList::RemoveAll()
155
/* Free up all the CNode objects NOTE we don't bother putting the
156
deleted nodes into the cache as this method is only really called
157
in serious times of change such as when we are being deleted at
158
which point the cache will be deleted anway */
160
CNode *pn = m_pFirst;
167
/* Reset the object count and the list pointers */
170
m_pFirst = m_pLast = NULL;
176
/* Return a position enumerator for the entire list.
177
A position enumerator is a pointer to a node object cast to a
178
transparent type so all we do is return the head/tail node
180
WARNING because the position is a pointer to a node there is
181
an implicit assumption for users a the list class that after
182
deleting an object from the list that any other position
183
enumerators that you have may be invalid (since the node
186
__out_opt POSITION CBaseList::GetHeadPositionI() const
188
return (POSITION) m_pFirst;
193
__out_opt POSITION CBaseList::GetTailPositionI() const
195
return (POSITION) m_pLast;
200
/* Get the number of objects in the list,
201
Get the lock before accessing the count.
202
Locking may not be entirely necessary but it has the side effect
203
of making sure that all operations are complete before we get it.
204
So for example if a list is being added to this list then that
205
will have completed in full before we continue rather than seeing
206
an intermediate albeit valid state
208
int CBaseList::GetCountI() const
215
/* Return the object at rp, update rp to the next object from
216
the list or NULL if you have moved over the last object.
217
You may still call this function once we return NULL but
218
we will continue to return a NULL position value
220
__out void *CBaseList::GetNextI(__inout POSITION& rp) const
222
/* have we reached the end of the list */
228
/* Lock the object before continuing */
232
/* Copy the original position then step on */
234
CNode *pn = (CNode *) rp;
236
rp = (POSITION) pn->Next();
238
/* Get the object at the original position from the list */
240
pObject = pn->GetData();
241
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
247
/* Return the object at p.
248
Asking for the object at NULL ASSERTs then returns NULL
249
The object is NOT locked. The list is not being changed
250
in any way. If another thread is busy deleting the object
251
then locking would only result in a change from one bad
252
behaviour to another.
254
__out_opt void *CBaseList::GetI(__in_opt POSITION p) const
260
CNode * pn = (CNode *) p;
261
void *pObject = pn->GetData();
262
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
266
__out void *CBaseList::GetValidI(__in POSITION p) const
268
CNode * pn = (CNode *) p;
269
void *pObject = pn->GetData();
270
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
275
/* Return the first position in the list which holds the given pointer.
276
Return NULL if it's not found.
278
__out_opt POSITION CBaseList::FindI( __in void * pObj) const
281
INTERNALTRAVERSELIST(*this, pn){
282
if (GetI(pn)==pObj) {
291
/* Remove the first node in the list (deletes the pointer to its object
292
from the list, does not free the object itself).
293
Return the pointer to its object or NULL if empty
295
__out_opt void *CBaseList::RemoveHeadI()
297
/* All we do is get the head position and ask for that to be deleted.
298
We could special case this since some of the code path checking
299
in Remove() is redundant as we know there is no previous
300
node for example but it seems to gain little over the
304
return RemoveI((POSITION)m_pFirst);
309
/* Remove the last node in the list (deletes the pointer to its object
310
from the list, does not free the object itself).
311
Return the pointer to its object or NULL if empty
313
__out_opt void *CBaseList::RemoveTailI()
315
/* All we do is get the tail position and ask for that to be deleted.
316
We could special case this since some of the code path checking
317
in Remove() is redundant as we know there is no previous
318
node for example but it seems to gain little over the
322
return RemoveI((POSITION)m_pLast);
327
/* Remove the pointer to the object in this position from the list.
328
Deal with all the chain pointers
329
Return a pointer to the object removed from the list.
330
The node object that is freed as a result
331
of this operation is added to the node cache where
332
it can be used again.
333
Remove(NULL) is a harmless no-op - but probably is a wart.
335
__out_opt void *CBaseList::RemoveI(__in_opt POSITION pos)
337
/* Lock the critical section before continuing */
339
// ASSERT (pos!=NULL); // Removing NULL is to be harmless!
340
if (pos==NULL) return NULL;
343
CNode *pCurrent = (CNode *) pos;
344
ASSERT(pCurrent != NULL);
346
/* Update the previous node */
348
CNode *pNode = pCurrent->Prev();
350
m_pFirst = pCurrent->Next();
352
pNode->SetNext(pCurrent->Next());
355
/* Update the following node */
357
pNode = pCurrent->Next();
359
m_pLast = pCurrent->Prev();
361
pNode->SetPrev(pCurrent->Prev());
364
/* Get the object this node was looking after */
366
void *pObject = pCurrent->GetData();
368
// ASSERT(pObject != NULL); // NULL pointers in the list are allowed.
370
/* Try and add the node object to the cache -
371
a NULL return code from the cache means we ran out of room.
372
The cache size is fixed by a constructor argument when the
373
list is created and defaults to DEFAULTCACHE.
374
This means that the cache will have room for this many
375
node objects. So if you have a list of media samples
376
and you know there will never be more than five active at
377
any given time of them for example then override the default
381
m_Cache.AddToCache(pCurrent);
383
/* If the list is empty then reset the list event */
386
ASSERT(m_Count >= 0);
392
/* Add this object to the tail end of our list
393
Return the new tail position.
396
__out_opt POSITION CBaseList::AddTailI(__in void *pObject)
398
/* Lock the critical section before continuing */
401
// ASSERT(pObject); // NULL pointers in the list are allowed.
403
/* If there is a node objects in the cache then use
404
that otherwise we will have to create a new one */
406
pNode = (CNode *) m_Cache.RemoveFromCache();
411
/* Check we have a valid object */
417
/* Initialise all the CNode object
418
just in case it came from the cache
421
pNode->SetData(pObject);
422
pNode->SetNext(NULL);
423
pNode->SetPrev(m_pLast);
425
if (m_pLast == NULL) {
428
m_pLast->SetNext(pNode);
431
/* Set the new last node pointer and also increment the number
432
of list entries, the critical section is unlocked when we
439
return (POSITION) pNode;
444
/* Add this object to the head end of our list
445
Return the new head position.
447
__out_opt POSITION CBaseList::AddHeadI(__in void *pObject)
450
// ASSERT(pObject); // NULL pointers in the list are allowed.
452
/* If there is a node objects in the cache then use
453
that otherwise we will have to create a new one */
455
pNode = (CNode *) m_Cache.RemoveFromCache();
460
/* Check we have a valid object */
466
/* Initialise all the CNode object
467
just in case it came from the cache
470
pNode->SetData(pObject);
472
/* chain it in (set four pointers) */
473
pNode->SetPrev(NULL);
474
pNode->SetNext(m_pFirst);
476
if (m_pFirst == NULL) {
479
m_pFirst->SetPrev(pNode);
485
return (POSITION) pNode;
490
/* Add all the elements in *pList to the tail of this list.
491
Return TRUE if it all worked, FALSE if it didn't.
492
If it fails some elements may have been added.
494
BOOL CBaseList::AddTail(__in CBaseList *pList)
496
/* lock the object before starting then enumerate
497
each entry in the source list and add them one by one to
498
our list (while still holding the object lock)
499
Lock the other list too.
501
POSITION pos = pList->GetHeadPositionI();
504
if (NULL == AddTailI(pList->GetNextI(pos))) {
513
/* Add all the elements in *pList to the head of this list.
514
Return TRUE if it all worked, FALSE if it didn't.
515
If it fails some elements may have been added.
517
BOOL CBaseList::AddHead(__in CBaseList *pList)
519
/* lock the object before starting then enumerate
520
each entry in the source list and add them one by one to
521
our list (while still holding the object lock)
522
Lock the other list too.
524
To avoid reversing the list, traverse it backwards.
529
INTERNALREVERSETRAVERSELIST(*pList, pos) {
530
if (NULL== AddHeadI(pList->GetValidI(pos))){
539
/* Add the object after position p
540
p is still valid after the operation.
541
AddAfter(NULL,x) adds x to the start - same as AddHead
542
Return the position of the new object, NULL if it failed
544
__out_opt POSITION CBaseList::AddAfterI(__in_opt POSITION pos, __in void * pObj)
547
return AddHeadI(pObj);
549
/* As someone else might be furkling with the list -
550
Lock the critical section before continuing
552
CNode *pAfter = (CNode *) pos;
553
ASSERT(pAfter != NULL);
555
return AddTailI(pObj);
557
/* set pnode to point to a new node, preferably from the cache */
559
CNode *pNode = (CNode *) m_Cache.RemoveFromCache();
564
/* Check we have a valid object */
570
/* Initialise all the CNode object
571
just in case it came from the cache
574
pNode->SetData(pObj);
576
/* It is to be added to the middle of the list - there is a before
577
and after node. Chain it after pAfter, before pBefore.
579
CNode * pBefore = pAfter->Next();
580
ASSERT(pBefore != NULL);
582
/* chain it in (set four pointers) */
583
pNode->SetPrev(pAfter);
584
pNode->SetNext(pBefore);
585
pBefore->SetPrev(pNode);
586
pAfter->SetNext(pNode);
590
return (POSITION) pNode;
592
} // AddAfter(object)
596
BOOL CBaseList::AddAfter(__in_opt POSITION p, __in CBaseList *pList)
599
INTERNALTRAVERSELIST(*pList, pos) {
600
/* p follows along the elements being added */
601
p = AddAfterI(p, pList->GetValidI(pos));
602
if (p==NULL) return FALSE;
610
Add the element or list after position p.
611
p is still valid after the operation.
612
AddBefore(NULL,x) adds x to the end - same as AddTail
614
__out_opt POSITION CBaseList::AddBeforeI(__in_opt POSITION pos, __in void * pObj)
617
return AddTailI(pObj);
619
/* set pnode to point to a new node, preferably from the cache */
621
CNode *pBefore = (CNode *) pos;
622
ASSERT(pBefore != NULL);
623
if (pBefore==m_pFirst)
624
return AddHeadI(pObj);
626
CNode * pNode = (CNode *) m_Cache.RemoveFromCache();
631
/* Check we have a valid object */
637
/* Initialise all the CNode object
638
just in case it came from the cache
641
pNode->SetData(pObj);
643
/* It is to be added to the middle of the list - there is a before
644
and after node. Chain it after pAfter, before pBefore.
647
CNode * pAfter = pBefore->Prev();
648
ASSERT(pAfter != NULL);
650
/* chain it in (set four pointers) */
651
pNode->SetPrev(pAfter);
652
pNode->SetNext(pBefore);
653
pBefore->SetPrev(pNode);
654
pAfter->SetNext(pNode);
658
return (POSITION) pNode;
660
} // Addbefore(object)
664
BOOL CBaseList::AddBefore(__in_opt POSITION p, __in CBaseList *pList)
667
INTERNALREVERSETRAVERSELIST(*pList, pos) {
668
/* p follows along the elements being added */
669
p = AddBeforeI(p, pList->GetValidI(pos));
670
if (p==NULL) return FALSE;
677
/* Split *this after position p in *this
678
Retain as *this the tail portion of the original *this
679
Add the head portion to the tail end of *pList
680
Return TRUE if it all worked, FALSE if it didn't.
683
foo->MoveToTail(foo->GetHeadPosition(), bar);
684
moves one element from the head of foo to the tail of bar
685
foo->MoveToTail(NULL, bar);
687
foo->MoveToTail(foo->GetTailPosition, bar);
688
concatenates foo onto the end of bar and empties foo.
690
A better, except excessively long name might be
691
MoveElementsFromHeadThroughPositionToOtherTail
693
BOOL CBaseList::MoveToTail
694
(__in_opt POSITION pos, __in CBaseList *pList)
697
Note that the elements (including their order) in the concatenation
698
of *pList to the head of *this is invariant.
699
1. Count elements to be moved
700
2. Join *pList onto the head of this to make one long chain
701
3. Set first/Last pointers in *this and *pList
702
4. Break the chain at the new place
704
6. Set/Reset any events
707
if (pos==NULL) return TRUE; // no-op. Eliminates special cases later.
710
/* Make cMove the number of nodes to move */
711
CNode * p = (CNode *)pos;
712
int cMove = 0; // number of nodes to move
719
/* Join the two chains together */
720
if (pList->m_pLast!=NULL)
721
pList->m_pLast->SetNext(m_pFirst);
723
m_pFirst->SetPrev(pList->m_pLast);
726
/* set first and last pointers */
729
if (pList->m_pFirst==NULL)
730
pList->m_pFirst = m_pFirst;
731
m_pFirst = p->Next();
737
/* Break the chain after p to create the new pieces */
739
m_pFirst->SetPrev(NULL);
743
/* Adjust the counts */
745
pList->m_Count += cMove;
753
/* Mirror image of MoveToTail:
754
Split *this before position p in *this.
755
Retain in *this the head portion of the original *this
756
Add the tail portion to the start (i.e. head) of *pList
757
Return TRUE if it all worked, FALSE if it didn't.
760
foo->MoveToHead(foo->GetTailPosition(), bar);
761
moves one element from the tail of foo to the head of bar
762
foo->MoveToHead(NULL, bar);
764
foo->MoveToHead(foo->GetHeadPosition, bar);
765
concatenates foo onto the start of bar and empties foo.
767
BOOL CBaseList::MoveToHead
768
(__in_opt POSITION pos, __in CBaseList *pList)
771
/* See the comments on the algorithm in MoveToTail */
773
if (pos==NULL) return TRUE; // no-op. Eliminates special cases later.
775
/* Make cMove the number of nodes to move */
776
CNode * p = (CNode *)pos;
777
int cMove = 0; // number of nodes to move
784
/* Join the two chains together */
785
if (pList->m_pFirst!=NULL)
786
pList->m_pFirst->SetPrev(m_pLast);
788
m_pLast->SetNext(pList->m_pFirst);
791
/* set first and last pointers */
795
if (pList->m_pLast==NULL)
796
pList->m_pLast = m_pLast;
804
/* Break the chain after p to create the new pieces */
806
m_pLast->SetNext(NULL);
810
/* Adjust the counts */
812
pList->m_Count += cMove;
820
/* Reverse the order of the [pointers to] objects in *this
822
void CBaseList::Reverse()
825
The obvious booby trap is that you flip pointers around and lose
826
addressability to the node that you are going to process next.
827
The easy way to avoid this is do do one chain at a time.
829
Run along the forward chain,
830
For each node, set the reverse pointer to the one ahead of us.
831
The reverse chain is now a copy of the old forward chain, including
832
the NULL termination.
834
Run along the reverse chain (i.e. old forward chain again)
835
For each node set the forward pointer of the node ahead to point back
836
to the one we're standing on.
837
The first node needs special treatment,
838
it's new forward pointer is NULL.
839
Finally set the First/Last pointers
844
// Yes we COULD use a traverse, but it would look funny!
849
p->SetNext(p->Prev());
861
if (m_pFirst==NULL) return; // empty list
862
if (m_pFirst->Next()==NULL) return; // single node list
865
/* run along forward chain */
870
p->SetPrev(p->Next());
874
/* special case first element */
875
m_pFirst->SetNext(NULL); // fix the old first element
878
/* run along new reverse chain i.e. old forward chain again */
879
for ( p = m_pFirst // start at the old first element
880
; p->Prev()!=NULL // while there's a node still to be set
881
; p = p->Prev() // work in the same direction as before
883
p->Prev()->SetNext(p);
887
/* fix forward and reverse pointers
888
- the triple XOR swap would work but all the casts look hideous */
896
#endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */