/*++ Copyright (c) 1998 Microsoft Corporation Module Name : pe_out.hxx Abstract: This module defines the outbound protocol event classes Author: Keith Lau (KeithLau) 6/18/98 Project: SMTP Server DLL Revision History: --*/ #ifndef _PE_OUT_HXX_ #define _PE_OUT_HXX_ #include "spinlock.h" // ================================================================= // Define a structure to hold an outbound binding node // typedef struct _PE_BINDING_NODE { struct _PE_BINDING_NODE *pNext; DWORD dwPriority; DWORD dwFlags; } PE_BINDING_NODE, *LPPE_BINDING_NODE; // // Flag indicating default binding (no sink) // #define PEBN_DEFAULT 0x00000001 // ================================================================= // Define a structue for a command node // Each command node has a list of binding nodes // typedef struct _PE_COMMAND_NODE { struct _PE_COMMAND_NODE *pNext; LPSTR pszCommandKeyword; DWORD dwHighestPriority; LPPE_BINDING_NODE pFirstBinding; } PE_COMMAND_NODE, *LPPE_COMMAND_NODE; // ================================================================= // Define a struct for a command queue entry. // typedef struct _OUTBOUND_COMMAND_Q_ENTRY { struct _OUTBOUND_COMMAND_Q_ENTRY *pNext; DWORD dwFlags; LPSTR pszFullCommand; LPSTR pszCommandKeyword; } OUTBOUND_COMMAND_Q_ENTRY, *LPOUTBOUND_COMMAND_Q_ENTRY; // // Some flag values for the queue entry // #define PECQ_PIPELINED 0x00000001 // ================================================================= // Fifo Queue, works as long as the first element is the link // // This also supports concurrent queue and dequeue as long as there // is a single writer and a single reader. // class CFifoQueueOld { public: CFifoQueueOld() { m_pHead = m_pTail = NULL; m_dwCount = 0; } BOOL IsEmpty() { return((!m_pHead)?TRUE:FALSE); } DWORD Length() { return(InterlockedExchangeAdd((PLONG)&m_dwCount, 0)); } LPVOID Dequeue() { if (!m_pHead) return(NULL); LPVOID pTemp = InterlockedExchangePointer(&m_pHead, *(LPVOID *)m_pHead); if (pTemp) { InterlockedCompareExchangePointer(&m_pTail, NULL, pTemp); LONG lTemp = InterlockedDecrement((PLONG)&m_dwCount); _ASSERT(lTemp >= 0); } return(pTemp); } BOOL Enqueue(LPVOID pItem) { _ASSERT(pItem); if (!pItem) return(FALSE); *(LPVOID *)pItem = NULL; if (InterlockedCompareExchangePointer(&m_pTail, pItem, NULL) == NULL) InterlockedExchangePointer(&m_pHead, pItem); else *(LPVOID *)(InterlockedExchangePointer(&m_pTail, pItem)) = pItem; InterlockedIncrement((PLONG)&m_dwCount); return(TRUE); } private: DWORD m_dwCount; LPVOID m_pHead; LPVOID m_pTail; }; class CFifoQueue { public: CFifoQueue() { m_pHead = m_pTail = NULL; m_dwCount = 0; InitializeSpinLock(&m_slock); } BOOL IsEmpty() { return((!m_pHead)?TRUE:FALSE); } DWORD Length() { return(m_dwCount); } LPVOID Dequeue() { LPVOID pRet; AcquireSpinLock(&m_slock); // // Return the head of the list // pRet = m_pHead; if(pRet) { // // Advance the head of the list // m_pHead = *((LPVOID *) m_pHead); m_dwCount--; _ASSERT(m_dwCount != (~0)); // // If the list is now empty, set the tail to NULL // if(m_pHead == NULL) m_pTail = NULL; } ReleaseSpinLock(&m_slock); return(pRet); } BOOL Enqueue(LPVOID pItem) { _ASSERT(pItem); if (!pItem) return(FALSE); // // Initialize the item's next pointer to NULL // *(LPVOID *)pItem = NULL; AcquireSpinLock(&m_slock); // // If the list is empty, initilize head and tail with the one // element // if(m_pTail == NULL) { _ASSERT(m_pHead == NULL); m_pHead = m_pTail = pItem; } else { // // Set the current tail's next pointer to the new item // *((LPVOID *) m_pTail) = pItem; // // Set the new tail // m_pTail = pItem; } m_dwCount++; ReleaseSpinLock(&m_slock); return(TRUE); } private: SPIN_LOCK m_slock; DWORD m_dwCount; LPVOID m_pHead; LPVOID m_pTail; }; // ================================================================= // Generic bound buffer // class CBoundAppendBuffer { public: CBoundAppendBuffer() { m_pBuffer = NULL; m_dwLength = 0; m_dwMaxLength = 0; } HRESULT SetBuffer( LPSTR pBuffer, DWORD dwMaxLength ) { if (!pBuffer || !dwMaxLength) return(E_POINTER); m_pBuffer = pBuffer; *m_pBuffer = '\0'; m_dwLength = 1; m_dwMaxLength = dwMaxLength; return(S_OK); } HRESULT Append( LPSTR pbAppendData, DWORD dwDataSize, DWORD *pdwNewSize ) { if (!m_pBuffer) return(E_POINTER); // If we have a buffer, the length should be > 0 _ASSERT(m_dwLength > 0); // // If the user is supplying a null terminated buffer, ignore // their null termination (terminate it ourselves) // if((dwDataSize > 0) && (pbAppendData[dwDataSize - 1] == '\0')) dwDataSize--; if ((dwDataSize + m_dwLength) > m_dwMaxLength) return(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); // We support a convention for NULL terminated strings _ASSERT(m_pBuffer[m_dwLength - 1] == '\0'); // Copy data and update counters CopyMemory(m_pBuffer + m_dwLength - 1, pbAppendData, dwDataSize); m_dwLength += dwDataSize; // Set the null termiantor m_pBuffer[m_dwLength - 1] = '\0'; if (pdwNewSize) *pdwNewSize = m_dwLength; return(S_OK); } BOOL SetLength(DWORD dwLength) { if ((dwLength > m_dwMaxLength) || (dwLength <= 0)) return(FALSE); m_dwLength = dwLength; return(TRUE); } DWORD Length() { return(m_dwLength); } DWORD MaxLength() { return(m_dwMaxLength); } LPSTR Buffer() { return(m_pBuffer); } void Reset() { m_dwLength = 0; if (m_pBuffer) { *m_pBuffer = '\0'; m_dwLength = 1; } } private: LPSTR m_pBuffer; DWORD m_dwLength; DWORD m_dwMaxLength; }; // // Variable sized buffer. Supports append mode only // #define _DEFAULT_APPEND_BUFFER_SIZE 256 // ================================================================= // Generic growable buffer // class CAppendBuffer { public: CAppendBuffer() { m_pBuffer = m_pbDefaultBuffer; *m_pBuffer = '\0'; m_dwLength = 1; m_dwMaxLength = _DEFAULT_APPEND_BUFFER_SIZE; } ~CAppendBuffer() { if (m_pBuffer != m_pbDefaultBuffer) delete [] m_pBuffer; } HRESULT Append( LPSTR pbAppendData, DWORD dwDataSize, DWORD *pdwNewSize ) { LPSTR pbNew; // // If the user passes in a null terminated buffer, ignore // their null termination // if((dwDataSize > 0) && (pbAppendData[dwDataSize - 1] == '\0')) dwDataSize--; if ((dwDataSize + m_dwLength) > m_dwMaxLength) { DWORD dwNewMax = m_dwMaxLength << 3; // Grow buffer by factor of 8 // Keep growing the buffer if it's still not large enough while( (dwNewMax != 0) && (dwNewMax < (dwDataSize + m_dwLength)) ) dwNewMax = dwNewMax << 1; if(dwNewMax == 0) { // We bit shifted right off the deep end. dwDataSize is > 2Gig return(E_OUTOFMEMORY); } pbNew = new char[dwNewMax]; if (!pbNew) return(E_OUTOFMEMORY); // Copy existing stuff over CopyMemory(pbNew, m_pBuffer, m_dwLength); m_dwMaxLength = dwNewMax; } else pbNew = m_pBuffer; // We support a convention for NULL terminated strings _ASSERT(m_dwLength > 0); _ASSERT(m_pBuffer[m_dwLength - 1] == '\0'); // Copy data and update counters CopyMemory(pbNew + m_dwLength - 1, pbAppendData, dwDataSize); m_dwLength += dwDataSize; // Set the NULL terminator pbNew[m_dwLength - 1] = '\0'; if (pdwNewSize) *pdwNewSize = m_dwLength; // Release the old buffer if (pbNew != m_pBuffer) { if (m_pBuffer != m_pbDefaultBuffer) delete [] m_pBuffer; m_pBuffer = pbNew; } return(S_OK); } DWORD Length() { return(m_dwLength); } DWORD MaxLength() { return(m_dwMaxLength); } LPSTR Buffer() { return(m_pBuffer); } void Reset() { m_dwLength = 1; *m_pBuffer = '\0'; } private: LPSTR m_pBuffer; DWORD m_dwLength; DWORD m_dwMaxLength; char m_pbDefaultBuffer[_DEFAULT_APPEND_BUFFER_SIZE]; }; #endif