[KS]
[reactos.git] / reactos / drivers / ksfilter / ks / bag.c
index bde0b33..87f7a2b 100644 (file)
@@ -9,21 +9,14 @@
 
 #include "priv.h"
 
-
-#if 0
 typedef struct
 {
-   LIST_ENTRY Entry;
-   PVOID Item;
-   PFNKSFREE Free;
-   LONG ReferenceCount;
-}KSOBJECT_BAG_ENTRY;
+    LIST_ENTRY Entry;
+    PVOID Item;
+    PFNKSFREE Free;
+    ULONG References;
+}KSIOBJECT_BAG_ENTRY, *PKSIOBJECT_BAG_ENTRY;
 
-typedef struct
-{
-    LIST_ENTRY ListHead;
-    KMUTEX Lock;
-}KSOBJECT_BAG_IMPL;
 
 /*
     @implemented
@@ -31,25 +24,153 @@ typedef struct
 KSDDKAPI
 NTSTATUS
 NTAPI
+KsAllocateObjectBag(
+    IN PKSDEVICE Device,
+    OUT KSOBJECT_BAG* ObjectBag)
+{
+    PKSIDEVICE_HEADER DeviceHeader;
+    PKSIOBJECT_BAG Bag;
+    IKsDevice *KsDevice;
+
+    /* get real device header */
+    DeviceHeader = (PKSIDEVICE_HEADER)CONTAINING_RECORD(Device, KSIDEVICE_HEADER, KsDevice);
+
+    /* allocate a object bag ctx */
+    Bag = AllocateItem(NonPagedPool, sizeof(KSIOBJECT_BAG));
+    if (!Bag)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* get device interface */
+    KsDevice = (IKsDevice*)&DeviceHeader->BasicHeader.OuterUnknown;
+
+    /* initialize object bag */
+    return KsDevice->lpVtbl->InitializeObjectBag(KsDevice, Bag, NULL);
+}
+
+PKSIOBJECT_BAG_ENTRY
+KspFindObjectBagItem(
+    IN PLIST_ENTRY ObjectList,
+    IN PVOID Item)
+{
+    PLIST_ENTRY Entry;
+    PKSIOBJECT_BAG_ENTRY BagEntry;
+
+    /* point to first item */
+    Entry = ObjectList->Flink;
+    /* first scan the list if the item is already inserted */
+    while(Entry != ObjectList)
+    {
+        /* get bag entry */
+        BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG_ENTRY, Entry);
+
+        if (BagEntry->Item == Item)
+        {
+            /* found entry */
+            return BagEntry;
+        }
+        /* move to next entry */
+        Entry = Entry->Flink;
+    }
+    /* item not in this object bag */
+    return NULL;
+}
+
+
+/*
+    @implemented
+*/
+NTSTATUS
+NTAPI
 KsAddItemToObjectBag(
-    IN KSOBJECT_BAG ObjectBag,
-    IN PVOID Item,
-    IN PFNKSFREE Free OPTIONAL)
+    IN KSOBJECT_BAG  ObjectBag,
+    IN PVOID  Item,
+    IN PFNKSFREE  Free  OPTIONAL)
 {
-    KSOBJECT_BAG_ENTRY * Entry;
-    KSOBJECT_BAG_IMPL * Bag = (KSOBJECT_BAG_IMPL)ObjectBag;
+    PKSIOBJECT_BAG Bag;
+    PKSIOBJECT_BAG_ENTRY BagEntry;
+
+    DPRINT("KsAddItemToObjectBag\n");
+
+    /* get real object bag */
+    Bag = (PKSIOBJECT_BAG)ObjectBag;
+
+    /* acquire bag mutex */
+    KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
+
+    /* is the item already present in this object bag */
+    BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
+
+    if (BagEntry)
+    {
+        /* is is, update reference count */
+        InterlockedIncrement((PLONG)&BagEntry->References);
+        /* release mutex */
+        KeReleaseMutex(Bag->BagMutex, FALSE);
+        /* return result */
+        return STATUS_SUCCESS;
+    }
 
-    Entry = ExAllocatePool(NonPagedPool, sizeof(KSOBJECT_BAG_ENTRY));
-    if (!Entry)
+    /* item is new, allocate entry */
+    BagEntry = AllocateItem(NonPagedPool, sizeof(KSIOBJECT_BAG_ENTRY));
+    if (!BagEntry)
+    {
+        /* no memory */
+        KeReleaseMutex(Bag->BagMutex, FALSE);
+        /* return result */
         return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-    Entry->Free = Free;
-    Entry->Item = Item;
+    /* initialize bag entry */
+    BagEntry->References = 1;
+    BagEntry->Item = Item;
+    if (Free)
+        BagEntry->Free = Free;
+    else
+        BagEntry->Free = ExFreePool;
 
-    InsertTailList(&Bag->ListHead, &Entry->Entry);
+    /* insert item */
+    InsertTailList(&Bag->ObjectList, &BagEntry->Entry);
+
+    /* release mutex */
+    KeReleaseMutex(Bag->BagMutex, FALSE);
+
+    /* done */
     return STATUS_SUCCESS;
 }
 
+ULONG
+KspGetObjectItemReferenceCount(
+    IN PKSIDEVICE_HEADER DeviceHeader,
+    IN PVOID Item)
+{
+    PLIST_ENTRY Entry;
+    PKSIOBJECT_BAG OtherBag;
+    PKSIOBJECT_BAG_ENTRY OtherBagEntry;
+    ULONG TotalRefs = 0;
+
+    /* scan all object bags and see if item is present there */
+    Entry = DeviceHeader->ObjectBags.Flink;
+    while(Entry != &DeviceHeader->ObjectBags)
+    {
+        /* get other bag */
+        OtherBag = (PKSIOBJECT_BAG)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
+
+        /* is the item present there */
+        OtherBagEntry = KspFindObjectBagItem(&OtherBag->ObjectList, Item);
+
+        if (OtherBagEntry)
+            TotalRefs++;
+
+        /* move to next item */
+        Entry = Entry->Flink;
+    }
+
+    return TotalRefs;
+}
+
+/*
+    @implemented
+*/
 KSDDKAPI
 ULONG
 NTAPI
@@ -58,10 +179,264 @@ KsRemoveItemFromObjectBag(
     IN PVOID Item,
     IN BOOLEAN Free)
 {
-    KSOBJECT_BAG_IMPL * Bag = (KSOBJECT_BAG_IMPL)ObjectBag;
+    PKSIOBJECT_BAG Bag;
+    PKSIOBJECT_BAG_ENTRY BagEntry;
+    ULONG TotalRefs;
+
+    /* get real object bag */
+    Bag = (PKSIOBJECT_BAG)ObjectBag;
 
-    
+    /* acquire bag mutex */
+    KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
 
+    /* is the item already present in this object bag */
+    BagEntry = KspFindObjectBagItem(&Bag->ObjectList, Item);
 
+    if (!BagEntry)
+    {
+        /* item was not in this object bag */
+        KeReleaseMutex(Bag->BagMutex, FALSE);
+        return 0;
+    }
+
+    /* set current refs count */
+    TotalRefs = BagEntry->References;
+
+    /* get total refs count */
+    TotalRefs += KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, Item);
+
+    /* decrease reference count */
+    InterlockedDecrement((PLONG)&BagEntry->References);
+
+    if (BagEntry->References == 0)
+    {
+        /* remove the entry */
+        RemoveEntryList(&BagEntry->Entry);
+    }
+
+    if (TotalRefs == 1)
+    {
+        /* does the caller want to free the item */
+        if (Free)
+        {
+            /* free the item */
+            BagEntry->Free(BagEntry->Item);
+        }
+    }
+    if (BagEntry->References == 0)
+    {
+        /* free bag item entry */
+        FreeItem(BagEntry);
+    }
+
+    /* release mutex */
+    KeReleaseMutex(Bag->BagMutex, FALSE);
+
+
+    return TotalRefs;
 }
-#endif
+
+/*
+    @implemented
+*/
+KSDDKAPI
+NTSTATUS
+NTAPI
+KsCopyObjectBagItems(
+    IN KSOBJECT_BAG ObjectBagDestination,
+    IN KSOBJECT_BAG ObjectBagSource)
+{
+    PKSIOBJECT_BAG ObjectBagDest, ObjectBagSrc;
+    PLIST_ENTRY Entry;
+    PKSIOBJECT_BAG_ENTRY BagEntry;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    /* get object bag src */
+    ObjectBagSrc = (PKSIOBJECT_BAG)ObjectBagSource;
+
+    /* get object bag dst */
+    ObjectBagDest = (PKSIOBJECT_BAG)ObjectBagDestination;
+
+    /* acquire source mutex */
+    KeWaitForSingleObject(ObjectBagSrc->BagMutex, Executive, KernelMode, FALSE, NULL);
+
+    if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
+    {
+        /* acquire destination mutex */
+        KeWaitForSingleObject(ObjectBagDest->BagMutex, Executive, KernelMode, FALSE, NULL);
+    }
+
+    /* point to first item */
+    Entry = ObjectBagSrc->ObjectList.Flink;
+    /* first scan the list if the item is already inserted */
+    while(Entry != &ObjectBagSrc->ObjectList)
+    {
+        /* get bag entry */
+        BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG_ENTRY, Entry);
+
+        /* add the item */
+        Status = KsAddItemToObjectBag(ObjectBagDestination, BagEntry->Item, BagEntry->Free);
+
+        /* check for success */
+        if (!NT_SUCCESS(Status))
+            break;
+
+        /* move to next entry */
+        Entry = Entry->Flink;
+    }
+
+    if (ObjectBagSrc->BagMutex != ObjectBagDest->BagMutex)
+    {
+        /* release destination mutex */
+        KeReleaseMutex(ObjectBagDest->BagMutex, FALSE);
+    }
+
+    /* release source mutex */
+     KeReleaseMutex(ObjectBagSrc->BagMutex, FALSE);
+
+    return Status;
+}
+
+/*
+    @implemented
+*/
+KSDDKAPI
+VOID
+NTAPI
+KsFreeObjectBag(
+    IN KSOBJECT_BAG ObjectBag)
+{
+    PLIST_ENTRY Entry;
+    PKSIOBJECT_BAG Bag;
+    PKSIOBJECT_BAG_ENTRY BagEntry;
+    ULONG TotalRefs;
+
+    /* get real object bag */
+    Bag = (PKSIOBJECT_BAG)ObjectBag;
+
+    /* acquire bag mutex */
+    KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
+
+    while(!IsListEmpty(&Bag->ObjectList))
+    {
+        /* get an bag entry */
+        Entry = RemoveHeadList(&Bag->ObjectList);
+        /* access bag entry item */
+        BagEntry = (PKSIOBJECT_BAG_ENTRY)CONTAINING_RECORD(Entry, KSIOBJECT_BAG, Entry);
+
+        /* check if the item is present in some other bag */
+        TotalRefs = KspGetObjectItemReferenceCount((PKSIDEVICE_HEADER)Bag->DeviceHeader, &BagEntry->Item);
+
+        if (TotalRefs == 0)
+        {
+            /* item is ready to be freed */
+            BagEntry->Free(BagEntry->Item);
+        }
+
+        /* free bag entry item */
+        FreeItem(BagEntry);
+    }
+
+    /* remove bag entry from device object list */
+    RemoveEntryList(&Bag->Entry);
+
+    /* release bag mutex */
+    KeReleaseMutex(Bag->BagMutex, FALSE);
+
+    /* now free object bag */
+    FreeItem(Bag);
+}
+
+/*
+    @implemented
+*/
+KSDDKAPI
+NTSTATUS
+NTAPI
+_KsEdit(
+    IN KSOBJECT_BAG ObjectBag,
+    IN OUT PVOID* PointerToPointerToItem,
+    IN ULONG NewSize,
+    IN ULONG OldSize,
+    IN ULONG Tag)
+{
+    PKSIOBJECT_BAG Bag;
+    PKSIOBJECT_BAG_ENTRY BagEntry;
+    PVOID Item;
+    NTSTATUS Status;
+
+    DPRINT("_KsEdit\n");
+
+    /* get real object bag */
+    Bag = (PKSIOBJECT_BAG)ObjectBag;
+
+    /* acquire bag mutex */
+    KeWaitForSingleObject(Bag->BagMutex, Executive, KernelMode, FALSE, NULL);
+
+
+    if (*PointerToPointerToItem)
+    {
+        /* search object bag for this entry */
+        BagEntry = KspFindObjectBagItem(&Bag->ObjectList, *PointerToPointerToItem);
+    }
+    else
+    {
+        /* pointer to null, allocate new entry */
+        BagEntry = NULL;
+    }
+
+    if (!BagEntry || NewSize > OldSize)
+    {
+        /* entry does not exist or new entry must be allocated */
+        Item = AllocateItem(NonPagedPool, NewSize);
+
+        if (!Item)
+        {
+            /* not enough resources */
+            KeReleaseMutex(Bag->BagMutex, FALSE);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* now add the item to the object bag */
+        Status = KsAddItemToObjectBag((KSOBJECT_BAG)Bag, Item, NULL);
+        /* check for success */
+        if (!NT_SUCCESS(Status))
+        {
+            /* failed to add item */
+            FreeItem(Item);
+            KeReleaseMutex(Bag->BagMutex, FALSE);
+            return Status;
+        }
+
+        if (*PointerToPointerToItem)
+        {
+            /* object exists */
+            if (OldSize >= NewSize)
+            {
+                /* copy old contents */
+                RtlMoveMemory(Item, *PointerToPointerToItem, NewSize);
+            }
+            else
+            {
+                /* copy new contents */
+                RtlMoveMemory(Item, *PointerToPointerToItem, OldSize);
+            }
+        }
+
+        if (BagEntry)
+        {
+            /* remove old entry */
+            KsRemoveItemFromObjectBag(ObjectBag, BagEntry->Item, TRUE);
+        }
+
+        /* store item */
+       *PointerToPointerToItem = Item;
+    }
+
+    /* release bag mutex */
+    KeReleaseMutex(Bag->BagMutex, FALSE);
+
+    return STATUS_SUCCESS;
+}
+
+