//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1995. // // File: psctbl2.hxx // // Contents: Trie-based IID to CLSID map // // Classes: CMapGuidToGuidBase // CPSClsidTbl // CScmPSClsidTbl // // History: 06-Jun-95 t-stevan Created // //-------------------------------------------------------------------------- #ifndef __GUIDMAP2_HXX__ #define __GUIDMAP2_HXX__ #include // *** Defines *** // Size of GUID in bytes const int GUID_BYTES =16; // Number of GUIDs per GUIDBlock const int cGuids = 16; // *** Types *** // handy types to pass around based pointers in, so that we don't have to // use __based all the time, since the usage of __based is severely limited typedef SmBasedPtr TrieNodeBasedPtr; typedef SmBasedPtr CGUIDBlockBasedPtr; typedef SmBasedPtr GUIDBasedPtr; // We reference each CLSID by a 16 bit index, so therefore we have a maximum // of 65,535 classids in the cache typedef WORD GUIDIndex; // an invalid GUIDIndex = 65535 const GUIDIndex INVALID_GUID_INDEX = 0xffff; // Functors for allocationg memory //+------------------------------------------------------------------------- // // Class: CAllocFunctor, CStackAlloc, CHeapAlloc // // Purpose: Used to isolate the exact memory allocator being used so that adding GUIDs to the map // works both when the map is in shared memory and in local memory // // Interface: appropriate constructors // Alloc // Free // // History: 05-Jul-95 t-stevan Created // // Note: These classes only represent an interface to a given allocator, they are not the allocators // themselves, so no resources are freed when these classes are destructed expect the room // used to hold the references to the other allocators // //-------------------------------------------------------------------------- class CAllocFunctor { public: virtual void *Alloc(ULONG cbSize)=0; virtual void Free(void *pvMem, ULONG cbSize)=0; }; // CStackAlloc - functor for CSmStackAllocator class CStackAlloc : public CAllocFunctor { public: CStackAlloc(CSmStackAllocator &stack) : m_stack(stack) {} void *Alloc(ULONG cbSize) { return m_stack.Alloc(cbSize); } void Free(void *pvMem, ULONG cbSize) { m_stack.Free(pvMem, cbSize); } private: CSmStackAllocator &m_stack; // use a reference }; // CHeapAlloc - functor for Heap* API calls class CHeapAlloc : public CAllocFunctor { public: CHeapAlloc(HANDLE hHeap) { m_hHeap = hHeap; } void *Alloc(ULONG cbSize) { return HeapAlloc(m_hHeap, 0, cbSize); } void Free(void *pvMem, ULONG) { HeapFree(m_hHeap, 0, pvMem); } private: HANDLE m_hHeap; }; // *** Externs *** // This string is used to hold the key name for IIDs in the registry extern TCHAR tszInterface[]; // the following string is used in compapi.cxx extern WCHAR wszProxyStubClsid[]; extern WCHAR wszProxyStubClsid16[]; //+------------------------------------------------------------------------- // // Class: CMapGuidToGuidBase // // Purpose: Trie-based GUID to GUID map, this is the base implementation // class for the two versions of the class - the DLL (client) version // and the EXE (server) version // // Interface: CGuidToGuidMapBase // ~CGuidToGuidMapBase // Initialize // // History: 06-Jun-95 t-stevan Created // // Note: This class is optimized for a IID->CLSID mapping, where there are many // IIDs and relatively few CLSIDs. So it represents a (usually) many-to-one mapping // Some efficiency would be lost in a one-to-one mapping. //-------------------------------------------------------------------------- class CMapGuidToGuidBase { public: inline CMapGuidToGuidBase(); // Our initialization funciton. HRESULT Initialize(void *pBase); protected: // *** Implementation *** // CGUIDBlock is a block of 16 GUIDs which hold the GUIDs which our key GUIDs are mapped TO // this is because there are a LOT of repeitions in a typical IID/CLSID mapping, so it's more // space economical to save a 2-byte index to one copy of a 16-byte GUID in each leaf, instead // of storing a 16 byte GUID. // Insertion into this chained list of blocks in linear time. Retrieval is O(1) (a pointer dereference). class CGUIDBlock { public: // space is allocated for us, so we don't use a constructor inline void Initialize(); GUIDIndex AddGuid(void *pBase, REFGUID guid, CAllocFunctor &alloc); HRESULT CopyToSharedMem(void *pNewBase, void *pOldBase, CGUIDBlock *pFirstBlock, CAllocFunctor &alloc); // returns the GUID a GUIDIndex maps to inline BOOL GetGuid(void *pBase, GUIDIndex guidIndex, GUID &guidOut) const; private: static GUIDIndex GuidInTable(void *pBase, CGUIDBlock *pBlock, REFGUID guid); int m_nGuids; // number of guids in this block CGUIDBlockBasedPtr m_bpNext; // pointer to next block in chained list GUID m_guidArray[cGuids]; // array of guids }; // Information that we keep in shared memory struct SharedMemHeader { CGUIDBlock m_GUIDs; TrieNodeBasedPtr m_bpTrie; // our dummy start node BOOL m_fFull; }; // Internal node structure // Variable length structure // Properties of Trie: // // (1) Each node in a trie is either an internal node (designated Node), // or a leaf node (designated Leaf). IsLeaf() returns this status // (2) Each Node has NumLinks() many links to other nodes // (3) Each Node has x amount of links allocated, such that // x = multiple of NODE_GROWBY greater than NumLinks() // unless # of links < NODE_LINKS, in this case x = NODE_LINKS // (4) The total size in bytes of a Node is sizeof(WORD)+NumKeyBytes()+sizeof(TrieNodeBasedPtr)*x, // where x is from (3) above // (5) Each Leaf has 0 links allocated, and sizeo(GUIDIndex) bytes for a GUID index, // which holds what the IID maps to. // (6) The total size in bytes of a Leaf is sizeof(WORD)+NumKeyBytes()+sizeof(GUIDIndex) // (7) Each Node and Leaf has NumKeyBytes() bytes allocated for storing parts of a key // NumKeyBytes() can be zero. // (8) Starting at the root of a trie, continuing down any path of links, will create a GUID // by taking the KeyBytes stored in each Node and the final Leaf in order. This is // the principle that Find is based on. // (9) Insertion involves going down the trie until a difference between the key being inserted, // and the existing key paths are found. When this happens, the Node or Leaf where the // difference occurred is split into three nodes : a prefix Node, holding the part of the // keys that were the same (garaunteed to be at least 1), a suffix Node or Leaf, holding the part // of the original key in the trie, and a new Leaf, holding the rest of the inserted key. // (10) Only leaves can be deleted, and thus have the Deleted flag set to 1 struct TrieNode { // Node info word allocated as follows: // // 15 14 13-5 4-0 // | D | F | L L L L L L L L L | K K K K K | m_wInfo // | | | ^---------- # of bytes of key stored at node (5 bits) // | | ^--------------------------- # of links to other nodes (9 bits) // | ^---------------------------------------- 1 = Data stored at node (leaf) (1 bit leaf flag) // | 0 = No data stored at node (non-leaf) // ^------------------------------------------- 1 = Node deleted (1 bit deleted flag) // 0 = Node not deleted // Note that the number of allocated links is // NODE_LINKS if NumLinks() < NODE_LINKS, or the closest multiple of NODE_GROWBY to NodeLinks() // these constants are defined in psctbl2.cxx WORD m_wInfo; BYTE m_bData[2]; // Placeholder for data // We use static functions and explicit "this" pointers (pRoot) // so that we can remove recursion easily by changing pRoot on the fly // Adds a key to a Trie starting at pRoot static HRESULT AddKey(void *pBase, TrieNode *pRoot, TrieNodeBasedPtr UNALIGNED *ppPrev, const BYTE *pbKey, GUIDIndex data, BOOL fReplace, CAllocFunctor &alloc); // copies a trie to shared memory and returns a based pointer to it static TrieNodeBasedPtr CopyToSharedMem(void *pNewBase, void *pOldBase, TrieNode *pRoot, CAllocFunctor &alloc); // skeleton code as to how to go about key removal is included, but // commented out. //static BOOL RemoveKey(TrieNode *pRoot, const BYTE *pbKey); // finds the data mapped to a key static BOOL TraverseKey(void *pBase, TrieNode *pRoot, const BYTE *pbKey, GUIDIndex &data); // public because members of CMapGuidToGuidBase and derived classes need to create leaves and nodes static TrieNode *CreateTrieLeaf(const BYTE *pbKey, BYTE bKeyBytes, GUIDIndex data, CAllocFunctor &alloc); static TrieNode *CreateTrieNode(const BYTE *pbKey, BYTE bKeyBytes, BYTE bLinks, CAllocFunctor &alloc); private: // *** Internal Functions *** // NOTE: All private inline TrieNode functions are implemented in psctbl2.cxx // and therefore only accessable from there inline int NumLinks() const; inline int NumKeyBytes() const; inline BOOL IsLeaf() const; inline BOOL IsDeleted() const; inline void SetLeaf(BOOL fLeaf); inline void SetDeleted(BOOL fDeleted); inline void SetKeyBytes(int nKeyBytes); inline void SetLinks(int nLinks); inline void IncrementLinks(); inline BYTE *GetKey(); inline GUIDIndex *GetData(); inline static int FindLinkSize(int nLinks); // Returns the computed size of a node given the information about the node inline static DWORD GetNodeSize(BYTE bKeyBytes, BOOL fLeaf, int bLinks); // return the start of the link array inline TrieNodeBasedPtr UNALIGNED *GetLinkStart() const; // Function which inserts a pointer into the link array // shifts nAfter nodes back one spot to make room inline static void InsertIntoLinkArray(TrieNodeBasedPtr UNALIGNED *pArray, TrieNodeBasedPtr pNode, int nAfter); // Returns S_OK if the node was added successfully // returns S_FALSE if the a new block of links had to be allocated // if this happens, a new TrieNode structure will be passed // in ppNewNode, and the old TrieNode structure will be invalid // This means any links to pRoot must be updated!!!! static HRESULT AddLink(void *pBase, TrieNode *pRoot, TrieNode *pNode, TrieNodeBasedPtr UNALIGNED *ppNewNode, CAllocFunctor &alloc); // Returns the TrieNode associated with the next byte in the key (bKey), // and also returns a pointer to where this link is stored. // This pointer (ppLink) is necessary so that if AddLink requires us to update // links to this retrieved node, we can do that. TrieNode *GetLink(void *pBase, BYTE bKey, TrieNodeBasedPtr UNALIGNED*&ppLink); // Creates a suffix node from an existing node, used for splitting tries up TrieNode *CreateSuffixNode(BYTE bBytesDifferent, CAllocFunctor &alloc); }; // *** Actual data for class *** void *m_pMemBase; // the base address for all our memory needs SharedMemHeader *m_pHeader; // our shared memory header }; //+------------------------------------------------------------------------- // // Class: CPSClsidTbl // // Purpose: Trie-based GUID to GUID map, this is the DLL (client) side // implementation, which only allows read-only operations // // Interface: Initialize // Find // // History: 22-Jun-95 t-stevan Created // // Note: Again, optimized for IID->CLSID maps //-------------------------------------------------------------------------- class CPSClsidTbl : public CMapGuidToGuidBase { public: HRESULT Initialize(void *pBase); HRESULT Find(REFGUID keyGuid, GUID *pGuidOut) const; inline BOOL IsFull() const; }; //+------------------------------------------------------------------------- // // Class: CScmPSClsidTbl // // Purpose: Trie-based GUID to GUID map, this is the EXE (server) side // implementation, which only allows write ops // // Interface: CScmPSClsidTbl // Initialize // InitTbl // FreeTbl // CopyToSharedMem // Add // IsFull // // History: 22-Jun-95 t-stevan Created // //-------------------------------------------------------------------------- class CScmPSClsidTbl : public CMapGuidToGuidBase { public: inline CScmPSClsidTbl(); // This initializes the object itself HRESULT Initialize(); // This loads in the entries to the table from the registry HRESULT InitTbl(); inline void FreeTbl(); // copies the locally built map to shared memory HRESULT CopyToSharedMem(void *pBase, CSmStackAllocator &alloc); // adds a key to the locally build map HRESULT AddLocal(REFGUID keyGuid, REFGUID dataGuid, BOOL fReplace=FALSE); //BOOL Delete(REFGUID keyGuid); inline BOOL IsFull() const; private: // Information we use to create the trie struct LocalMemHeader { CGUIDBlock m_GUIDs; TrieNodeBasedPtr m_bpTrie; BOOL m_fFull; }; LocalMemHeader *m_pLocalHeader; // local tree information HANDLE m_hHeap; // local heap used to construct the table }; // *** Inline Functions *** //+------------------------------------------------------------------------- // // Function: CMapGuidToGuidBase::CMapGuidToGuidBase // // Synopsis: Constructor of GUID->GUID map // // Arguments: none // // Returns: nothing // //-------------------------------------------------------------------------- CMapGuidToGuidBase::CMapGuidToGuidBase() : m_pMemBase(NULL), m_pHeader(NULL) { } //+------------------------------------------------------------------------- // // Member: CPSClsidTbl::IsFull // // Synopsis: Returns if the map is full // // Arguments: (none) // // Returns: TRUE if the map is full, FALSE is not // //-------------------------------------------------------------------------- inline BOOL CPSClsidTbl::IsFull() const { if(m_pHeader != NULL) { return m_pHeader->m_fFull; } else { Win4Assert(0 && "CPSClsidTbl::IsFull() called with no valid header!"); return TRUE; } } //+------------------------------------------------------------------------- // // Function: CScmPSClsidTbl::CScmPSClsidTbl // // Synopsis: Constructor of GUID->GUID map, server side // // Arguments: none // // Returns: nothing // //-------------------------------------------------------------------------- CScmPSClsidTbl::CScmPSClsidTbl() : m_pLocalHeader(NULL), m_hHeap(NULL) { } //+------------------------------------------------------------------------- // // Member: CScmPSClsidTbl::IsFull // // Synopsis: Returns if the map is full // // Arguments: (none) // // Returns: TRUE if the map is full, FALSE is not // //-------------------------------------------------------------------------- inline BOOL CScmPSClsidTbl::IsFull() const { if(m_pLocalHeader != NULL) { return m_pLocalHeader->m_fFull; } else if(m_pHeader != NULL) { return m_pHeader->m_fFull; } else { Win4Assert(0 && "CScmPSClsidTbl::IsFull() called with no valid header!"); return TRUE; } } //+------------------------------------------------------------------------- // // Function: CScmPSClsidTbl::FreeTbl // // Synopsis: Frees up the locally allocated table // // Arguments: none // // Returns: nothing // //-------------------------------------------------------------------------- void CScmPSClsidTbl::FreeTbl() { Win4Assert(m_hHeap != NULL); HeapDestroy(m_hHeap); m_hHeap = NULL; } #endif // __GUIDMAP2_HXX__