/* * R G I T E R . H * * Range iterator */ #ifndef _EX_RGITER_H_ #define _EX_RGITER_H_ #pragma warning(disable:4200) // zero-sized array // Ranges -------------------------------------------------------------------- // enum { RANGE_TOTAL_UNKNOWN = 0xFFFFFFFF, RANGE_NOT_PRESENT = 0xFFFFFFFF, RANGE_UNKNOWN = 0, RANGE_ROW, RANGE_URL, RANGE_FIND }; // Range Items --------------------------------------------------------------- // // There are two different range item formats. // // row/byte ranges = DWRGITEM; // url/find ranges = SZRGITEM; // typedef struct _dwrgitem { DWORD dwFirst; // first row/byte of a range DWORD dwLast; // last row/byte of a range } DWRGITEM; typedef struct _szrgitem { LONG lcRows; // count of rows to return DWORD cb; // length, in bytes of item including NULL and padding WCHAR wsz[]; // item padded out to align as needed } SZRGITEM; typedef struct _rgitem { DWORD uRT; // range type SCODE sc; union { DWRGITEM dwrgi; // item for byte and row ranges SZRGITEM szrgi; // item for url and find ranges }; } RGITEM, *PRGITEM; inline DWORD CbRangeItem (const RGITEM * prgi) { Assert (prgi); DWORD cb = sizeof(RGITEM); if ((RANGE_URL == prgi->uRT) || (RANGE_FIND == prgi->uRT)) cb += prgi->szrgi.cb; return cb; } // Range Classes ------------------------------------------------------------- // // There are two classes for dealing with ranges. A class that constructs the // range item array (a range parser), and a class that iterates over a range // array. // // It is important to note that the CRangeParser only is used to parse the HTTP // "Range" header. This header does not support the syntax of url and/or find // ranges, so the parser only builds items of type "bytes" and/or "rows". // // Since both of these share the same format (DWRGITEM), and it is a fixed size, // there are some simplifying assumptions that can be made without adding too // much complexity to the parser. // // Both parser and iterator share a common base... // class CRangeBase { protected: // Count of ranges parsed out. // DWORD m_cRGList; // Index of the range that is currently being parsed and/or processed // DWORD m_iCur; RGITEM * m_prgi; // An array of ranges of size m_cRCList. As noted above, this array is // built up from items that were parsed from the HTTP header, and can // then be assumed to be a fixed size based on the count of ranges. This // is an important aspect of the CRangeParser. // auto_heap_ptr m_pbData; DWORD m_cbSize; // Collapsing unknown ranges // void CollapseUnknown(); // NOT IMPLEMENTED // CRangeBase& operator=( const CRangeBase& ); CRangeBase( const CRangeBase& ); public: ~CRangeBase(); CRangeBase() : m_cRGList(0), m_cbSize(0), m_iCur(0), m_prgi(0) { } // Range fixup. There are some cases where ranges need to be fixed up // to match the actual amount of bytes/rows available. Note that this // only impacts byte and/or row ranges. // SCODE ScFixupRanges (DWORD dwCount); // Advances through the rangGet the next range. // const RGITEM * PrgiNextRange(); // Rewind to the first range. // void Rewind() { m_iCur = 0; m_prgi = NULL; } // Check for more ranges // BOOL FMoreRanges () const { return m_iCur < m_cRGList; } // Check if a range present or not // BOOL FRangePresent (DWORD dw) const { return RANGE_NOT_PRESENT != dw; } // Gets the total number of ranges. // ULONG UlTotalRanges() const { return m_cRGList; } // Return the range array, with count and size. // RGITEM * PrgRangeArray( /* [out] */ ULONG * pulCount, /* [out] */ ULONG * pulSize, /* [in] */ BOOL fTakeOwnership) { Assert (pulCount); Assert (pulSize); RGITEM * prgi = reinterpret_cast (fTakeOwnership ? m_pbData.relinquish() : m_pbData.get()); *pulCount = m_cRGList; *pulSize = m_cbSize; return prgi; } }; class CRangeParser : public CRangeBase { private: // NOT IMPLEMENTED // CRangeParser& operator=( const CRangeParser& ); CRangeParser( const CRangeParser& ); public: CRangeParser() {} ~CRangeParser(); // Takes a range header and builds an array of ranges. Calls // ScParseRangeHdr() to perform syntax checking, then validates // the ranges against the entity size. // SCODE ScParseByteRangeHdr (LPCWSTR pwszRgHeader, DWORD dwSize); // Take a range header and builds an array of ranges. Performs // syntax checking. // SCODE ScParseRangeHdr (LPCWSTR pwszRgHeader, LPCWSTR pwszRangeUnit); }; class CRangeIter : public CRangeBase { private: // NOT IMPLEMENTED // CRangeIter& operator=( const CRangeIter& ); CRangeIter( const CRangeIter& ); public: CRangeIter() {} ~CRangeIter(); // Initialize a range iteration object based off of an existing // range data blob. In this case, the blob is copied and not consumed // by the call. // SCODE ScInit (ULONG cRGList, const RGITEM * prgRGList, ULONG cbSize); // Initialize a range iteration object based off of an existing // range data blob. In this case, the blob is consumed by the new // object. // SCODE ScInit (CRangeParser& crp) { RGITEM * prgi = crp.PrgRangeArray (&m_cRGList, &m_cbSize, TRUE /* fTakeOwnership */); m_pbData = reinterpret_cast(prgi); // Rewind all the state. // Rewind(); return S_OK; } }; // Range Parsing ------------------------------------------------------------- // SCODE ScParseOneWideRange ( /* [in] */ LPCWSTR pwsz, /* [out] */ DWORD * pdwStart, /* [out] */ DWORD * pdwEnd); // Range support ------------------------------------------------------------- // // Helper function to tell whether a range is a special range (0,0xffffffff) // which is used to represent the rows(bytes)=-n range on a zero sized response // body. // inline BOOL FSpecialRangeForZeroSizedBody (RGITEM * prgItem) { Assert (prgItem); return ((RANGE_ROW == prgItem->uRT) && (0 == prgItem->dwrgi.dwFirst) && (RANGE_NOT_PRESENT == prgItem->dwrgi.dwLast)); } // Range emitting ------------------------------------------------------------ // SCODE ScGenerateContentRange ( /* [in] */ LPCSTR pszRangeUnit, /* [in] */ const RGITEM * prgRGList, /* [in] */ ULONG cRanges, /* [in] */ ULONG cbRanges, /* [in] */ ULONG ulTotal, /* [out] */ LPSTR *ppszContentRange); #endif // _EX_RGITER_H_