2
0

FileIOHelper.cpp 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // FileIOHelper.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. // TODO: consider including an empty blob singleton (possibly UTF-8/16 too). //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #include "dxc/Support/Global.h"
  12. #include "dxc/Support/WinIncludes.h"
  13. #include "dxc/Support/microcom.h"
  14. #include "dxc/Support/Unicode.h"
  15. #include "dxc/Support/FileIOHelper.h"
  16. #include "dxc/Support/WinFunctions.h"
  17. #include "dxc/dxcapi.h"
  18. #include <algorithm>
  19. #include <memory>
  20. #ifdef _WIN32
  21. #include <intsafe.h>
  22. #endif
  23. #define CP_UTF16 1200
  24. struct HeapMalloc : public IMalloc {
  25. public:
  26. ULONG STDMETHODCALLTYPE AddRef() override { return 1; }
  27. ULONG STDMETHODCALLTYPE Release() override { return 1; }
  28. STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override {
  29. return DoBasicQueryInterface<IMalloc>(this, iid, ppvObject);
  30. }
  31. virtual void *STDMETHODCALLTYPE Alloc (
  32. /* [annotation][in] */
  33. _In_ SIZE_T cb) override {
  34. return HeapAlloc(GetProcessHeap(), 0, cb);
  35. }
  36. virtual void *STDMETHODCALLTYPE Realloc (
  37. /* [annotation][in] */
  38. _In_opt_ void *pv,
  39. /* [annotation][in] */
  40. _In_ SIZE_T cb) override
  41. {
  42. return HeapReAlloc(GetProcessHeap(), 0, pv, cb);
  43. }
  44. virtual void STDMETHODCALLTYPE Free (
  45. /* [annotation][in] */
  46. _In_opt_ void *pv) override
  47. {
  48. HeapFree(GetProcessHeap(), 0, pv);
  49. }
  50. virtual SIZE_T STDMETHODCALLTYPE GetSize(
  51. /* [annotation][in] */
  52. _In_opt_ _Post_writable_byte_size_(return) void *pv)
  53. {
  54. return HeapSize(GetProcessHeap(), 0, pv);
  55. }
  56. virtual int STDMETHODCALLTYPE DidAlloc(
  57. /* [annotation][in] */
  58. _In_opt_ void *pv)
  59. {
  60. return -1; // don't know
  61. }
  62. virtual void STDMETHODCALLTYPE HeapMinimize(void) {}
  63. };
  64. static HeapMalloc g_HeapMalloc;
  65. namespace hlsl {
  66. IMalloc *GetGlobalHeapMalloc() throw() {
  67. return &g_HeapMalloc;
  68. }
  69. _Use_decl_annotations_
  70. void ReadBinaryFile(IMalloc *pMalloc, LPCWSTR pFileName, void **ppData,
  71. DWORD *pDataSize) {
  72. HANDLE hFile = CreateFileW(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
  73. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
  74. if (hFile == INVALID_HANDLE_VALUE) {
  75. IFT(HRESULT_FROM_WIN32(GetLastError()));
  76. }
  77. CHandle h(hFile);
  78. LARGE_INTEGER FileSize;
  79. if (!GetFileSizeEx(hFile, &FileSize)) {
  80. IFT(HRESULT_FROM_WIN32(GetLastError()));
  81. }
  82. if (FileSize.u.HighPart != 0) {
  83. throw(hlsl::Exception(DXC_E_INPUT_FILE_TOO_LARGE, "input file is too large"));
  84. }
  85. char *pData = (char *)pMalloc->Alloc(FileSize.u.LowPart);
  86. if (!pData) {
  87. throw std::bad_alloc();
  88. }
  89. DWORD BytesRead;
  90. if (!ReadFile(hFile, pData, FileSize.u.LowPart, &BytesRead, nullptr)) {
  91. HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
  92. pMalloc->Free(pData);
  93. throw ::hlsl::Exception(hr);
  94. }
  95. DXASSERT(FileSize.u.LowPart == BytesRead, "ReadFile operation failed");
  96. *ppData = pData;
  97. *pDataSize = FileSize.u.LowPart;
  98. }
  99. _Use_decl_annotations_
  100. void ReadBinaryFile(LPCWSTR pFileName, void **ppData, DWORD *pDataSize) {
  101. return ReadBinaryFile(GetGlobalHeapMalloc(), pFileName, ppData, pDataSize);
  102. }
  103. _Use_decl_annotations_
  104. void WriteBinaryFile(LPCWSTR pFileName, const void *pData, DWORD DataSize) {
  105. HANDLE hFile = CreateFileW(pFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
  106. if(hFile == INVALID_HANDLE_VALUE) {
  107. IFT(HRESULT_FROM_WIN32(GetLastError()));
  108. }
  109. CHandle h(hFile);
  110. DWORD BytesWritten;
  111. if(!WriteFile(hFile, pData, DataSize, &BytesWritten, nullptr)) {
  112. IFT(HRESULT_FROM_WIN32(GetLastError()));
  113. }
  114. DXASSERT(DataSize == BytesWritten, "WriteFile operation failed");
  115. }
  116. _Use_decl_annotations_
  117. UINT32 DxcCodePageFromBytes(const char *bytes, size_t byteLen) throw() {
  118. UINT32 codePage;
  119. if (byteLen >= 4) {
  120. // Now try to use the BOM to check for Unicode encodings
  121. char bom[4] = { bytes[0], bytes[1], bytes[2], bytes[3] };
  122. if (strncmp(bom, "\xef\xbb\xbf", 3) == 0) {
  123. codePage = CP_UTF8;
  124. }
  125. else if (strncmp(bom, "\xff\xfe\x00\x00", 4) == 0) {
  126. codePage = 12000; //UTF-32 LE
  127. }
  128. else if (strncmp(bom, "\x00\x00\xfe\xff", 4) == 0) {
  129. codePage = 12001; //UTF-32 BE
  130. }
  131. else if (strncmp(bom, "\xff\xfe", 2) == 0) {
  132. codePage = 1200; //UTF-16 LE
  133. }
  134. else if (strncmp(bom, "\xfe\xff", 2) == 0) {
  135. codePage = 1201; //UTF-16 BE
  136. }
  137. else {
  138. codePage = CP_ACP;
  139. }
  140. }
  141. else {
  142. codePage = CP_ACP;
  143. }
  144. return codePage;
  145. }
  146. #define IsSizeWcharAligned(size) (((size) & (sizeof(wchar_t) - 1)) == 0)
  147. template<typename _char>
  148. bool IsUtfBufferNullTerminated(LPCVOID pBuffer, SIZE_T size) {
  149. return (size >= sizeof(_char) && (size & (sizeof(_char) - 1)) == 0 &&
  150. reinterpret_cast<const _char*>(pBuffer)[(size / sizeof(_char)) - 1] == 0);
  151. }
  152. static bool IsBufferNullTerminated(LPCVOID pBuffer, SIZE_T size, UINT32 codePage) {
  153. switch (codePage) {
  154. case DXC_CP_UTF8: return IsUtfBufferNullTerminated<char>(pBuffer, size);
  155. case DXC_CP_UTF16: return IsUtfBufferNullTerminated<wchar_t>(pBuffer, size);
  156. default: return false;
  157. }
  158. }
  159. template<typename _char>
  160. bool IsUtfBufferEmptyString(LPCVOID pBuffer, SIZE_T size) {
  161. return (size == 0 || (size == sizeof(_char) &&
  162. reinterpret_cast<const _char*>(pBuffer)[0] == 0));
  163. }
  164. static bool IsBufferEmptyString(LPCVOID pBuffer, SIZE_T size, UINT32 codePage) {
  165. switch (codePage) {
  166. case DXC_CP_UTF8: return IsUtfBufferEmptyString<char>(pBuffer, size);
  167. case DXC_CP_UTF16: return IsUtfBufferEmptyString<wchar_t>(pBuffer, size);
  168. default: return IsUtfBufferEmptyString<char>(pBuffer, size);
  169. }
  170. }
  171. class DxcBlobNoEncoding_Impl : public IDxcBlobEncoding {
  172. public:
  173. typedef IDxcBlobEncoding Base;
  174. static const UINT32 CodePage = CP_ACP;
  175. };
  176. class DxcBlobUtf16_Impl : public IDxcBlobUtf16 {
  177. public:
  178. static const UINT32 CodePage = CP_UTF16;
  179. typedef IDxcBlobUtf16 Base;
  180. virtual LPCWSTR STDMETHODCALLTYPE GetStringPointer(void) override {
  181. if (GetBufferSize() < sizeof(wchar_t)) {
  182. return L""; // Special case for empty string blob
  183. }
  184. DXASSERT(IsSizeWcharAligned(GetBufferSize()),
  185. "otherwise, buffer size is not even multiple of wchar_t");
  186. DXASSERT(*(const wchar_t*)
  187. ((const BYTE*)GetBufferPointer() + GetBufferSize() - sizeof(wchar_t))
  188. == L'\0',
  189. "otherwise buffer is not null terminated.");
  190. return (LPCWSTR)GetBufferPointer();
  191. }
  192. virtual SIZE_T STDMETHODCALLTYPE GetStringLength(void) override {
  193. SIZE_T bufSize = GetBufferSize();
  194. return bufSize ? (bufSize / sizeof(wchar_t)) - 1 : 0;
  195. }
  196. };
  197. class DxcBlobUtf8_Impl : public IDxcBlobUtf8 {
  198. public:
  199. static const UINT32 CodePage = CP_UTF8;
  200. typedef IDxcBlobUtf8 Base;
  201. virtual LPCSTR STDMETHODCALLTYPE GetStringPointer(void) override {
  202. if (GetBufferSize() < sizeof(char)) {
  203. return ""; // Special case for empty string blob
  204. }
  205. DXASSERT(*((const char*)GetBufferPointer() + GetBufferSize() - 1) == '\0',
  206. "otherwise buffer is not null terminated.");
  207. return (LPCSTR)GetBufferPointer();
  208. }
  209. virtual SIZE_T STDMETHODCALLTYPE GetStringLength(void) override {
  210. SIZE_T bufSize = GetBufferSize();
  211. return bufSize ? (bufSize / sizeof(char)) - 1 : 0;
  212. }
  213. };
  214. template <typename _T>
  215. class InternalDxcBlobEncoding_Impl : public _T {
  216. private:
  217. DXC_MICROCOM_TM_REF_FIELDS() // an underlying m_pMalloc that owns this
  218. LPCVOID m_Buffer = nullptr;
  219. IUnknown* m_Owner = nullptr; // IMalloc when MallocFree is true, owning the buffer
  220. SIZE_T m_BufferSize;
  221. unsigned m_EncodingKnown : 1;
  222. unsigned m_MallocFree : 1;
  223. UINT32 m_CodePage;
  224. public:
  225. DXC_MICROCOM_ADDREF_IMPL(m_dwRef)
  226. ULONG STDMETHODCALLTYPE Release() override {
  227. // Because blobs are also used by tests and utilities, we avoid using TLS.
  228. ULONG result = (ULONG)--m_dwRef;
  229. if (result == 0) {
  230. CComPtr<IMalloc> pTmp(m_pMalloc);
  231. this->~InternalDxcBlobEncoding_Impl();
  232. pTmp->Free(this);
  233. }
  234. return result;
  235. }
  236. DXC_MICROCOM_TM_CTOR(InternalDxcBlobEncoding_Impl)
  237. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  238. return DoBasicQueryInterface<IDxcBlob, IDxcBlobEncoding, typename _T::Base>(this, iid, ppvObject);
  239. }
  240. ~InternalDxcBlobEncoding_Impl() {
  241. if (m_MallocFree) {
  242. ((IMalloc *)m_Owner)->Free(const_cast<void *>(m_Buffer));
  243. }
  244. if (m_Owner != nullptr) {
  245. m_Owner->Release();
  246. }
  247. }
  248. static HRESULT
  249. CreateFromBlob(_In_ IDxcBlob *pBlob, _In_ IMalloc *pMalloc, bool encodingKnown, UINT32 codePage,
  250. _COM_Outptr_ InternalDxcBlobEncoding_Impl **pEncoding) {
  251. *pEncoding = InternalDxcBlobEncoding_Impl::Alloc(pMalloc);
  252. if (*pEncoding == nullptr) {
  253. return E_OUTOFMEMORY;
  254. }
  255. DXASSERT(_T::CodePage == CP_ACP || (encodingKnown && _T::CodePage == codePage), "encoding must match type");
  256. pBlob->AddRef();
  257. (*pEncoding)->m_Owner = pBlob;
  258. (*pEncoding)->m_Buffer = pBlob->GetBufferPointer();
  259. (*pEncoding)->m_BufferSize = pBlob->GetBufferSize();
  260. (*pEncoding)->m_EncodingKnown = encodingKnown;
  261. (*pEncoding)->m_MallocFree = 0;
  262. (*pEncoding)->m_CodePage = codePage;
  263. (*pEncoding)->AddRef();
  264. return S_OK;
  265. }
  266. static HRESULT
  267. CreateFromMalloc(LPCVOID buffer, IMalloc *pIMalloc, SIZE_T bufferSize, bool encodingKnown,
  268. UINT32 codePage, _COM_Outptr_ InternalDxcBlobEncoding_Impl **pEncoding) {
  269. *pEncoding = InternalDxcBlobEncoding_Impl::Alloc(pIMalloc);
  270. if (*pEncoding == nullptr) {
  271. *pEncoding = nullptr;
  272. return E_OUTOFMEMORY;
  273. }
  274. DXASSERT(_T::CodePage == CP_ACP || (encodingKnown && _T::CodePage == codePage), "encoding must match type");
  275. DXASSERT(buffer || bufferSize == 0, "otherwise, nullptr with non-zero size provided");
  276. pIMalloc->AddRef();
  277. (*pEncoding)->m_Owner = pIMalloc;
  278. (*pEncoding)->m_Buffer = buffer;
  279. (*pEncoding)->m_BufferSize = bufferSize;
  280. (*pEncoding)->m_EncodingKnown = encodingKnown;
  281. (*pEncoding)->m_MallocFree = buffer != nullptr;
  282. (*pEncoding)->m_CodePage = codePage;
  283. (*pEncoding)->AddRef();
  284. return S_OK;
  285. }
  286. void AdjustPtrAndSize(unsigned offset, unsigned size) {
  287. DXASSERT(offset < m_BufferSize, "else caller will overflow");
  288. DXASSERT(offset + size <= m_BufferSize, "else caller will overflow");
  289. m_Buffer = (const uint8_t*)m_Buffer + offset;
  290. m_BufferSize = size;
  291. }
  292. virtual LPVOID STDMETHODCALLTYPE GetBufferPointer(void) override {
  293. return const_cast<LPVOID>(m_Buffer);
  294. }
  295. virtual SIZE_T STDMETHODCALLTYPE GetBufferSize(void) override {
  296. return m_BufferSize;
  297. }
  298. virtual HRESULT STDMETHODCALLTYPE GetEncoding(_Out_ BOOL *pKnown, _Out_ UINT32 *pCodePage) override {
  299. *pKnown = m_EncodingKnown ? TRUE : FALSE;
  300. *pCodePage = m_CodePage;
  301. return S_OK;
  302. }
  303. // Relatively dangerous API. This means the buffer should be pinned for as
  304. // long as this object is alive.
  305. void ClearFreeFlag() { m_MallocFree = 0; }
  306. };
  307. typedef InternalDxcBlobEncoding_Impl<DxcBlobNoEncoding_Impl> InternalDxcBlobEncoding;
  308. typedef InternalDxcBlobEncoding_Impl<DxcBlobUtf16_Impl> InternalDxcBlobUtf16;
  309. typedef InternalDxcBlobEncoding_Impl<DxcBlobUtf8_Impl> InternalDxcBlobUtf8;
  310. static HRESULT CodePageBufferToUtf16(UINT32 codePage, LPCVOID bufferPointer,
  311. SIZE_T bufferSize,
  312. CDxcMallocHeapPtr<WCHAR> &utf16NewCopy,
  313. _Out_ UINT32 *pConvertedCharCount) {
  314. *pConvertedCharCount = 0;
  315. // If the buffer is empty, don't dereference bufferPointer at all.
  316. // Keep the null terminator post-condition.
  317. if (IsBufferEmptyString(bufferPointer, bufferSize, codePage)) {
  318. if (!utf16NewCopy.Allocate(1))
  319. return E_OUTOFMEMORY;
  320. utf16NewCopy.m_pData[0] = L'\0';
  321. DXASSERT(*pConvertedCharCount == 0, "else didn't init properly");
  322. return S_OK;
  323. }
  324. // Calculate the length of the buffer in wchar_t elements.
  325. int numToConvertUTF16 =
  326. MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, (LPCSTR)bufferPointer,
  327. bufferSize, nullptr, 0);
  328. if (numToConvertUTF16 == 0)
  329. return HRESULT_FROM_WIN32(GetLastError());
  330. // Add an extra character in case we need it for null-termination
  331. unsigned buffSizeUTF16;
  332. IFR(Int32ToUInt32(numToConvertUTF16, &buffSizeUTF16));
  333. IFR(UInt32Add(buffSizeUTF16, 1, &buffSizeUTF16));
  334. IFR(UInt32Mult(buffSizeUTF16, sizeof(WCHAR), &buffSizeUTF16));
  335. utf16NewCopy.AllocateBytes(buffSizeUTF16);
  336. IFROOM(utf16NewCopy.m_pData);
  337. int numActuallyConvertedUTF16 =
  338. MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, (LPCSTR)bufferPointer,
  339. bufferSize, utf16NewCopy, buffSizeUTF16);
  340. if (numActuallyConvertedUTF16 == 0)
  341. return HRESULT_FROM_WIN32(GetLastError());
  342. if (numActuallyConvertedUTF16 < 0)
  343. return E_OUTOFMEMORY;
  344. // If all we have is null terminator, return with zero count.
  345. if (utf16NewCopy.m_pData[0] == L'\0') {
  346. DXASSERT(*pConvertedCharCount == 0, "else didn't init properly");
  347. return S_OK;
  348. }
  349. if ((UINT32)numActuallyConvertedUTF16 < (buffSizeUTF16 / sizeof(wchar_t)) &&
  350. utf16NewCopy.m_pData[numActuallyConvertedUTF16 - 1] != L'\0') {
  351. utf16NewCopy.m_pData[numActuallyConvertedUTF16++] = L'\0';
  352. }
  353. *pConvertedCharCount = (UINT32)numActuallyConvertedUTF16;
  354. return S_OK;
  355. }
  356. static HRESULT CodePageBufferToUtf8(UINT32 codePage, LPCVOID bufferPointer,
  357. SIZE_T bufferSize,
  358. IMalloc *pMalloc,
  359. CDxcMallocHeapPtr<char> &utf8NewCopy,
  360. _Out_ UINT32 *pConvertedCharCount) {
  361. *pConvertedCharCount = 0;
  362. CDxcMallocHeapPtr<WCHAR> utf16NewCopy(pMalloc);
  363. UINT32 utf16CharCount = 0;
  364. const WCHAR *utf16Chars = nullptr;
  365. if (codePage == CP_UTF16) {
  366. DXASSERT(IsSizeWcharAligned(bufferSize), "otherwise, odd buffer size with UTF-16");
  367. utf16Chars = (const WCHAR*)bufferPointer;
  368. utf16CharCount = bufferSize / sizeof(wchar_t);
  369. } else if (bufferSize) {
  370. IFR(CodePageBufferToUtf16(codePage, bufferPointer, bufferSize,
  371. utf16NewCopy, &utf16CharCount));
  372. utf16Chars = utf16NewCopy.m_pData;
  373. }
  374. // If the buffer is empty, don't dereference bufferPointer at all.
  375. // Keep the null terminator post-condition.
  376. if (IsUtfBufferEmptyString<wchar_t>(utf16Chars, utf16CharCount)) {
  377. if (!utf8NewCopy.Allocate(1))
  378. return E_OUTOFMEMORY;
  379. DXASSERT(*pConvertedCharCount == 0, "else didn't init properly");
  380. utf8NewCopy.m_pData[0] = '\0';
  381. return S_OK;
  382. }
  383. int numToConvertUtf8 =
  384. WideCharToMultiByte(CP_UTF8, 0, utf16Chars, utf16CharCount,
  385. NULL, 0, NULL, NULL);
  386. if (numToConvertUtf8 == 0)
  387. return HRESULT_FROM_WIN32(GetLastError());
  388. UINT32 buffSizeUtf8;
  389. IFR(Int32ToUInt32(numToConvertUtf8, &buffSizeUtf8));
  390. if (!IsBufferNullTerminated(utf16Chars, utf16CharCount * sizeof(wchar_t), CP_UTF16)) {
  391. // If original size doesn't include null-terminator,
  392. // we have to add one to the converted buffer.
  393. IFR(UInt32Add(buffSizeUtf8, 1, &buffSizeUtf8));
  394. }
  395. utf8NewCopy.AllocateBytes(buffSizeUtf8);
  396. IFROOM(utf8NewCopy.m_pData);
  397. int numActuallyConvertedUtf8 =
  398. WideCharToMultiByte(CP_UTF8, 0, utf16Chars, utf16CharCount,
  399. utf8NewCopy, buffSizeUtf8, NULL, NULL);
  400. if (numActuallyConvertedUtf8 == 0)
  401. return HRESULT_FROM_WIN32(GetLastError());
  402. if (numActuallyConvertedUtf8 < 0)
  403. return E_OUTOFMEMORY;
  404. if ((UINT32)numActuallyConvertedUtf8 < buffSizeUtf8 &&
  405. utf8NewCopy.m_pData[numActuallyConvertedUtf8 - 1] != '\0') {
  406. utf8NewCopy.m_pData[numActuallyConvertedUtf8++] = '\0';
  407. }
  408. *pConvertedCharCount = (UINT32)numActuallyConvertedUtf8;
  409. return S_OK;
  410. }
  411. static bool TryCreateEmptyBlobUtf(
  412. UINT32 codePage, IMalloc *pMalloc, IDxcBlobEncoding **ppBlobEncoding) {
  413. if (codePage == CP_UTF8) {
  414. InternalDxcBlobUtf8 *internalUtf8;
  415. IFR(InternalDxcBlobUtf8::CreateFromMalloc(
  416. nullptr, pMalloc, 0, true, codePage, &internalUtf8));
  417. *ppBlobEncoding = internalUtf8;
  418. return true;
  419. } else if (codePage == CP_UTF16) {
  420. InternalDxcBlobUtf16 *internalUtf16;
  421. IFR(InternalDxcBlobUtf16::CreateFromMalloc(
  422. nullptr, pMalloc, 0, true, codePage, &internalUtf16));
  423. *ppBlobEncoding = internalUtf16;
  424. return true;
  425. }
  426. return false;
  427. }
  428. static bool TryCreateBlobUtfFromBlob(
  429. IDxcBlob *pFromBlob, UINT32 codePage, IMalloc *pMalloc,
  430. IDxcBlobEncoding **ppBlobEncoding) {
  431. // Try to create a IDxcBlobUtf8 or IDxcBlobUtf16
  432. if (IsBlobNullOrEmpty(pFromBlob)) {
  433. return TryCreateEmptyBlobUtf(codePage, pMalloc, ppBlobEncoding);
  434. } else if (IsBufferNullTerminated(pFromBlob->GetBufferPointer(),
  435. pFromBlob->GetBufferSize(), codePage)) {
  436. if (codePage == CP_UTF8) {
  437. InternalDxcBlobUtf8 *internalUtf8;
  438. IFR(InternalDxcBlobUtf8::CreateFromBlob(
  439. pFromBlob, pMalloc, true, codePage, &internalUtf8));
  440. *ppBlobEncoding = internalUtf8;
  441. return true;
  442. } else if (codePage == CP_UTF16) {
  443. InternalDxcBlobUtf16 *internalUtf16;
  444. IFR(InternalDxcBlobUtf16::CreateFromBlob(
  445. pFromBlob, pMalloc, true, codePage, &internalUtf16));
  446. *ppBlobEncoding = internalUtf16;
  447. return true;
  448. }
  449. }
  450. return false;
  451. }
  452. HRESULT DxcCreateBlob(
  453. LPCVOID pPtr, SIZE_T size, bool bPinned, bool bCopy,
  454. bool encodingKnown, UINT32 codePage,
  455. IMalloc *pMalloc, IDxcBlobEncoding **ppBlobEncoding) throw() {
  456. IFRBOOL(!(bPinned && bCopy), E_INVALIDARG);
  457. IFRBOOL(ppBlobEncoding, E_INVALIDARG);
  458. *ppBlobEncoding = nullptr;
  459. bool bNullTerminated = encodingKnown ? IsBufferNullTerminated(pPtr, size, codePage) : false;
  460. if (!pMalloc)
  461. pMalloc = DxcGetThreadMallocNoRef();
  462. // Handle empty blob
  463. if (!pPtr || !size) {
  464. if (encodingKnown && TryCreateEmptyBlobUtf(codePage, pMalloc, ppBlobEncoding))
  465. return S_OK;
  466. InternalDxcBlobEncoding *pInternalEncoding;
  467. IFR(InternalDxcBlobEncoding::CreateFromMalloc(nullptr, pMalloc, 0, encodingKnown, codePage, &pInternalEncoding));
  468. *ppBlobEncoding = pInternalEncoding;
  469. }
  470. if (bPinned) {
  471. if (encodingKnown) {
  472. if (bNullTerminated) {
  473. if (codePage == CP_UTF8) {
  474. InternalDxcBlobUtf8 *internalUtf8;
  475. IFR(InternalDxcBlobUtf8::CreateFromMalloc(
  476. pPtr, pMalloc, size, true, codePage, &internalUtf8));
  477. *ppBlobEncoding = internalUtf8;
  478. internalUtf8->ClearFreeFlag();
  479. return S_OK;
  480. } else if (codePage == CP_UTF16) {
  481. InternalDxcBlobUtf16 *internalUtf16;
  482. IFR(InternalDxcBlobUtf16::CreateFromMalloc(
  483. pPtr, pMalloc, size, true, codePage, &internalUtf16));
  484. *ppBlobEncoding = internalUtf16;
  485. internalUtf16->ClearFreeFlag();
  486. return S_OK;
  487. }
  488. }
  489. }
  490. InternalDxcBlobEncoding *internalEncoding;
  491. IFR(InternalDxcBlobEncoding::CreateFromMalloc(
  492. pPtr, pMalloc, size, encodingKnown, codePage, &internalEncoding));
  493. *ppBlobEncoding = internalEncoding;
  494. internalEncoding->ClearFreeFlag();
  495. return S_OK;
  496. }
  497. void *pData = const_cast<void*>(pPtr);
  498. SIZE_T newSize = size;
  499. CDxcMallocHeapPtr<char> heapCopy(pMalloc);
  500. if (bCopy) {
  501. if (encodingKnown) {
  502. if (!bNullTerminated) {
  503. if (codePage == CP_UTF8) {
  504. newSize += sizeof(char);
  505. bNullTerminated = true;
  506. } else if (codePage == CP_UTF16) {
  507. newSize += sizeof(wchar_t);
  508. bNullTerminated = true;
  509. }
  510. }
  511. }
  512. heapCopy.AllocateBytes(newSize);
  513. pData = heapCopy.m_pData;
  514. if (pData == nullptr)
  515. return E_OUTOFMEMORY;
  516. if (pPtr)
  517. memcpy(pData, pPtr, size);
  518. else
  519. memset(pData, 0, size);
  520. }
  521. if (bNullTerminated && codePage == CP_UTF8) {
  522. if (bCopy && newSize > size)
  523. ((char*)pData)[newSize - 1] = 0;
  524. InternalDxcBlobUtf8 *internalUtf8;
  525. IFR(InternalDxcBlobUtf8::CreateFromMalloc(
  526. pData, pMalloc, newSize, true, codePage, &internalUtf8));
  527. *ppBlobEncoding = internalUtf8;
  528. } else if (bNullTerminated && codePage == CP_UTF16) {
  529. if (bCopy && newSize > size)
  530. ((wchar_t*)pData)[(newSize / sizeof(wchar_t)) - 1] = 0;
  531. InternalDxcBlobUtf16 *internalUtf16;
  532. IFR(InternalDxcBlobUtf16::CreateFromMalloc(
  533. pData, pMalloc, newSize, true, codePage, &internalUtf16));
  534. *ppBlobEncoding = internalUtf16;
  535. } else {
  536. InternalDxcBlobEncoding *internalEncoding;
  537. IFR(InternalDxcBlobEncoding::CreateFromMalloc(
  538. pData, pMalloc, newSize, encodingKnown, codePage, &internalEncoding));
  539. *ppBlobEncoding = internalEncoding;
  540. }
  541. if (bCopy)
  542. heapCopy.Detach();
  543. return S_OK;
  544. }
  545. HRESULT DxcCreateBlobEncodingFromBlob(
  546. IDxcBlob *pFromBlob, UINT32 offset, UINT32 length,
  547. bool encodingKnown, UINT32 codePage,
  548. IMalloc *pMalloc, IDxcBlobEncoding **ppBlobEncoding) throw() {
  549. IFRBOOL(pFromBlob, E_POINTER);
  550. IFRBOOL(ppBlobEncoding, E_POINTER);
  551. *ppBlobEncoding = nullptr;
  552. if (!pMalloc)
  553. pMalloc = DxcGetThreadMallocNoRef();
  554. InternalDxcBlobEncoding *internalEncoding;
  555. if (offset || length) {
  556. UINT32 end;
  557. IFR(UInt32Add(offset, length, &end));
  558. SIZE_T blobSize = pFromBlob->GetBufferSize();
  559. if (end > blobSize)
  560. return E_INVALIDARG;
  561. IFR(InternalDxcBlobEncoding::CreateFromBlob(
  562. pFromBlob, pMalloc, encodingKnown, codePage, &internalEncoding));
  563. internalEncoding->AdjustPtrAndSize(offset, length);
  564. *ppBlobEncoding = internalEncoding;
  565. return S_OK;
  566. }
  567. if (!encodingKnown || codePage == CP_UTF8) {
  568. IDxcBlobUtf8 *pBlobUtf8;
  569. if(SUCCEEDED(pFromBlob->QueryInterface(&pBlobUtf8))) {
  570. *ppBlobEncoding = pBlobUtf8;
  571. return S_OK;
  572. }
  573. }
  574. if (!encodingKnown || codePage == CP_UTF16) {
  575. IDxcBlobUtf16 *pBlobUtf16;
  576. if (SUCCEEDED(pFromBlob->QueryInterface(&pBlobUtf16))) {
  577. *ppBlobEncoding = pBlobUtf16;
  578. return S_OK;
  579. }
  580. }
  581. CComPtr<IDxcBlobEncoding> pBlobEncoding;
  582. if (SUCCEEDED(pFromBlob->QueryInterface(&pBlobEncoding))) {
  583. BOOL thisEncodingKnown;
  584. UINT32 thisEncoding;
  585. IFR(pBlobEncoding->GetEncoding(&thisEncodingKnown, &thisEncoding));
  586. bool encodingMatches = thisEncodingKnown && encodingKnown &&
  587. codePage == thisEncoding;
  588. if (!encodingKnown && thisEncodingKnown) {
  589. codePage = thisEncoding;
  590. encodingKnown = thisEncodingKnown;
  591. encodingMatches = true;
  592. }
  593. if (encodingMatches) {
  594. if (!TryCreateBlobUtfFromBlob(pFromBlob, codePage, pMalloc, ppBlobEncoding)) {
  595. *ppBlobEncoding = pBlobEncoding.Detach();
  596. }
  597. return S_OK;
  598. }
  599. if (encodingKnown) {
  600. IFR(InternalDxcBlobEncoding::CreateFromBlob(
  601. pFromBlob, pMalloc, true, codePage, &internalEncoding));
  602. *ppBlobEncoding = internalEncoding;
  603. return S_OK;
  604. }
  605. DXASSERT(!encodingKnown && !thisEncodingKnown, "otherwise, missing case");
  606. *ppBlobEncoding = pBlobEncoding.Detach();
  607. return S_OK;
  608. }
  609. if (encodingKnown && TryCreateBlobUtfFromBlob(pFromBlob, codePage, pMalloc, ppBlobEncoding)) {
  610. return S_OK;
  611. }
  612. IFR(InternalDxcBlobEncoding::CreateFromBlob(
  613. pFromBlob, pMalloc, encodingKnown, codePage, &internalEncoding));
  614. *ppBlobEncoding = internalEncoding;
  615. return S_OK;
  616. }
  617. _Use_decl_annotations_
  618. HRESULT DxcCreateBlobFromBlob(
  619. IDxcBlob *pBlob, UINT32 offset, UINT32 length, IDxcBlob **ppResult) throw() {
  620. IFRBOOL(ppResult, E_POINTER);
  621. *ppResult = nullptr;
  622. IDxcBlobEncoding *pResult;
  623. IFR(DxcCreateBlobEncodingFromBlob(pBlob, offset, length, false, 0, DxcGetThreadMallocNoRef(), &pResult));
  624. *ppResult = pResult;
  625. return S_OK;
  626. }
  627. _Use_decl_annotations_
  628. HRESULT
  629. DxcCreateBlobOnMalloc(LPCVOID pData, IMalloc *pIMalloc, UINT32 size, IDxcBlob **ppResult) throw() {
  630. IFRBOOL(ppResult, E_POINTER);
  631. *ppResult = nullptr;
  632. IDxcBlobEncoding *pResult;
  633. IFR(DxcCreateBlob(pData, size, false, false, false, 0, pIMalloc, &pResult));
  634. *ppResult = pResult;
  635. return S_OK;
  636. }
  637. _Use_decl_annotations_
  638. HRESULT
  639. DxcCreateBlobOnHeapCopy(_In_bytecount_(size) LPCVOID pData, UINT32 size,
  640. _COM_Outptr_ IDxcBlob **ppResult) throw() {
  641. IFRBOOL(ppResult, E_POINTER);
  642. *ppResult = nullptr;
  643. IDxcBlobEncoding *pResult;
  644. IFR(DxcCreateBlob(pData, size, false, true, false, 0, DxcGetThreadMallocNoRef(), &pResult));
  645. *ppResult = pResult;
  646. return S_OK;
  647. }
  648. _Use_decl_annotations_
  649. HRESULT
  650. DxcCreateBlobFromFile(IMalloc *pMalloc, LPCWSTR pFileName, UINT32 *pCodePage,
  651. IDxcBlobEncoding **ppBlobEncoding) throw() {
  652. if (pFileName == nullptr || ppBlobEncoding == nullptr) {
  653. return E_POINTER;
  654. }
  655. LPVOID pData;
  656. DWORD dataSize;
  657. *ppBlobEncoding = nullptr;
  658. try {
  659. ReadBinaryFile(pMalloc, pFileName, &pData, &dataSize);
  660. }
  661. CATCH_CPP_RETURN_HRESULT();
  662. bool known = (pCodePage != nullptr);
  663. UINT32 codePage = (pCodePage != nullptr) ? *pCodePage : 0;
  664. HRESULT hr = DxcCreateBlob(pData, dataSize, false, false, known, codePage, pMalloc, ppBlobEncoding);
  665. if (FAILED(hr))
  666. pMalloc->Free(pData);
  667. return hr;
  668. }
  669. _Use_decl_annotations_
  670. HRESULT DxcCreateBlobFromFile(LPCWSTR pFileName, UINT32 *pCodePage,
  671. IDxcBlobEncoding **ppBlobEncoding) throw() {
  672. return DxcCreateBlobFromFile(DxcGetThreadMallocNoRef(), pFileName, pCodePage, ppBlobEncoding);
  673. }
  674. _Use_decl_annotations_
  675. HRESULT
  676. DxcCreateBlobWithEncodingSet(IMalloc *pMalloc, IDxcBlob *pBlob, UINT32 codePage,
  677. IDxcBlobEncoding **ppBlobEncoding) throw() {
  678. return DxcCreateBlobEncodingFromBlob(pBlob, 0, 0, true, codePage, pMalloc, ppBlobEncoding);
  679. }
  680. _Use_decl_annotations_
  681. HRESULT
  682. DxcCreateBlobWithEncodingSet(IDxcBlob *pBlob, UINT32 codePage,
  683. IDxcBlobEncoding **ppBlobEncoding) throw() {
  684. return DxcCreateBlobEncodingFromBlob(pBlob, 0, 0, true, codePage, nullptr, ppBlobEncoding);
  685. }
  686. _Use_decl_annotations_
  687. HRESULT DxcCreateBlobWithEncodingFromPinned(LPCVOID pText, UINT32 size,
  688. UINT32 codePage,
  689. IDxcBlobEncoding **pBlobEncoding) throw() {
  690. return DxcCreateBlob(pText, size, true, false, true, codePage, nullptr, pBlobEncoding);
  691. }
  692. HRESULT DxcCreateBlobFromPinned(
  693. _In_bytecount_(size) LPCVOID pText, UINT32 size,
  694. _COM_Outptr_ IDxcBlob **pBlob) throw() {
  695. CComPtr<IDxcBlobEncoding> pBlobEncoding;
  696. DxcCreateBlob(pText, size, true, false, false, CP_ACP, nullptr, &pBlobEncoding);
  697. return pBlobEncoding.QueryInterface(pBlob);
  698. }
  699. _Use_decl_annotations_
  700. HRESULT
  701. DxcCreateBlobWithEncodingFromStream(IStream *pStream, bool newInstanceAlways,
  702. UINT32 codePage,
  703. IDxcBlobEncoding **ppBlobEncoding) throw() {
  704. IFRBOOL(ppBlobEncoding, E_POINTER);
  705. *ppBlobEncoding = nullptr;
  706. if (pStream == nullptr) {
  707. return S_OK;
  708. }
  709. // Try to reuse the existing stream.
  710. if (!newInstanceAlways) {
  711. CComPtr<IDxcBlobEncoding> blobEncoding;
  712. if (SUCCEEDED(pStream->QueryInterface(&blobEncoding))) {
  713. *ppBlobEncoding = blobEncoding.Detach();
  714. return S_OK;
  715. }
  716. }
  717. // Layer over the blob if possible.
  718. CComPtr<IDxcBlob> blob;
  719. if (SUCCEEDED(pStream->QueryInterface(&blob))) {
  720. return DxcCreateBlobWithEncodingSet(blob, codePage, ppBlobEncoding);
  721. }
  722. // Create a copy of contents, last resort.
  723. // TODO: implement when we find this codepath internally
  724. return E_NOTIMPL;
  725. }
  726. _Use_decl_annotations_
  727. HRESULT
  728. DxcCreateBlobWithEncodingOnHeapCopy(LPCVOID pText, UINT32 size, UINT32 codePage,
  729. IDxcBlobEncoding **pBlobEncoding) throw() {
  730. return DxcCreateBlob(pText, size, false, true, true, codePage, nullptr, pBlobEncoding);
  731. }
  732. _Use_decl_annotations_
  733. HRESULT
  734. DxcCreateBlobWithEncodingOnMalloc(LPCVOID pText, IMalloc *pIMalloc, UINT32 size, UINT32 codePage,
  735. IDxcBlobEncoding **pBlobEncoding) throw() {
  736. return DxcCreateBlob(pText, size, false, false, true, codePage, pIMalloc, pBlobEncoding);
  737. }
  738. _Use_decl_annotations_
  739. HRESULT
  740. DxcCreateBlobWithEncodingOnMallocCopy(IMalloc *pIMalloc, LPCVOID pText, UINT32 size, UINT32 codePage,
  741. IDxcBlobEncoding **ppBlobEncoding) throw() {
  742. return DxcCreateBlob(pText, size, false, true, true, codePage, pIMalloc, ppBlobEncoding);
  743. }
  744. _Use_decl_annotations_
  745. HRESULT DxcGetBlobAsUtf8(IDxcBlob *pBlob, IMalloc *pMalloc, IDxcBlobUtf8 **pBlobEncoding) throw() {
  746. IFRBOOL(pBlob, E_POINTER);
  747. IFRBOOL(pBlobEncoding, E_POINTER);
  748. *pBlobEncoding = nullptr;
  749. if (SUCCEEDED(pBlob->QueryInterface(pBlobEncoding)))
  750. return S_OK;
  751. HRESULT hr;
  752. CComPtr<IDxcBlobEncoding> pSourceBlob;
  753. UINT32 codePage = CP_ACP;
  754. BOOL known = FALSE;
  755. if (SUCCEEDED(pBlob->QueryInterface(&pSourceBlob))) {
  756. if (FAILED(hr = pSourceBlob->GetEncoding(&known, &codePage)))
  757. return hr;
  758. }
  759. SIZE_T blobLen = pBlob->GetBufferSize();
  760. if (!known) {
  761. codePage = DxcCodePageFromBytes((char *)pBlob->GetBufferPointer(), blobLen);
  762. }
  763. if (!pMalloc)
  764. pMalloc = DxcGetThreadMallocNoRef();
  765. CDxcMallocHeapPtr<char> utf8NewCopy(pMalloc);
  766. UINT32 utf8CharCount = 0;
  767. // Reuse or copy the underlying blob depending on null-termination
  768. if (codePage == CP_UTF8) {
  769. utf8CharCount = blobLen;
  770. if (IsBufferNullTerminated(pBlob->GetBufferPointer(), blobLen, CP_UTF8)) {
  771. // Already null-terminated, reference other blob's memory
  772. InternalDxcBlobUtf8* internalEncoding;
  773. hr = InternalDxcBlobUtf8::CreateFromBlob(pBlob, pMalloc, true, CP_UTF8, &internalEncoding);
  774. if (SUCCEEDED(hr)) {
  775. *pBlobEncoding = internalEncoding;
  776. }
  777. return hr;
  778. } else {
  779. // Copy to new buffer and null-terminate
  780. if(!utf8NewCopy.Allocate(utf8CharCount + 1))
  781. return E_OUTOFMEMORY;
  782. memcpy(utf8NewCopy.m_pData, pBlob->GetBufferPointer(), blobLen);
  783. utf8NewCopy.m_pData[utf8CharCount++] = 0;
  784. }
  785. } else {
  786. // Convert and create a blob that owns the encoding.
  787. if (FAILED(
  788. hr = CodePageBufferToUtf8(codePage, pBlob->GetBufferPointer(), blobLen,
  789. pMalloc, utf8NewCopy, &utf8CharCount))) {
  790. return hr;
  791. }
  792. DXASSERT(!utf8CharCount ||
  793. IsBufferNullTerminated(utf8NewCopy.m_pData, utf8CharCount, CP_UTF8),
  794. "otherwise, CodePageBufferToUtf8 failed to null-terminate buffer.");
  795. }
  796. // At this point, we have new utf8NewCopy to wrap in a blob
  797. InternalDxcBlobUtf8* internalEncoding;
  798. hr = InternalDxcBlobUtf8::CreateFromMalloc(
  799. utf8NewCopy.m_pData, pMalloc,
  800. utf8CharCount, true, CP_UTF8, &internalEncoding);
  801. if (SUCCEEDED(hr)) {
  802. *pBlobEncoding = internalEncoding;
  803. utf8NewCopy.Detach();
  804. }
  805. return hr;
  806. }
  807. // This is kept for compatibility.
  808. HRESULT
  809. DxcGetBlobAsUtf8NullTerm(_In_ IDxcBlob *pBlob,
  810. _COM_Outptr_ IDxcBlobEncoding **ppBlobEncoding) throw() {
  811. IFRBOOL(pBlob, E_POINTER);
  812. IFRBOOL(ppBlobEncoding, E_POINTER);
  813. *ppBlobEncoding = nullptr;
  814. CComPtr<IDxcBlobUtf8> pConverted;
  815. IFR(DxcGetBlobAsUtf8(pBlob, DxcGetThreadMallocNoRef(), &pConverted));
  816. pConverted->QueryInterface(ppBlobEncoding);
  817. return S_OK;
  818. }
  819. _Use_decl_annotations_
  820. HRESULT DxcGetBlobAsUtf16(IDxcBlob *pBlob, IMalloc *pMalloc, IDxcBlobUtf16 **pBlobEncoding) throw() {
  821. IFRBOOL(pBlob, E_POINTER);
  822. IFRBOOL(pBlobEncoding, E_POINTER);
  823. *pBlobEncoding = nullptr;
  824. if (SUCCEEDED(pBlob->QueryInterface(pBlobEncoding)))
  825. return S_OK;
  826. HRESULT hr;
  827. CComPtr<IDxcBlobEncoding> pSourceBlob;
  828. UINT32 codePage = CP_ACP;
  829. BOOL known = FALSE;
  830. if (SUCCEEDED(pBlob->QueryInterface(&pSourceBlob))) {
  831. if (FAILED(hr = pSourceBlob->GetEncoding(&known, &codePage)))
  832. return hr;
  833. }
  834. SIZE_T blobLen = pBlob->GetBufferSize();
  835. if (!known) {
  836. codePage = DxcCodePageFromBytes((char *)pBlob->GetBufferPointer(), blobLen);
  837. }
  838. if (!pMalloc)
  839. pMalloc = DxcGetThreadMallocNoRef();
  840. CDxcMallocHeapPtr<WCHAR> utf16NewCopy(pMalloc);
  841. UINT32 utf16CharCount = 0;
  842. // Reuse or copy the underlying blob depending on null-termination
  843. if (codePage == CP_UTF16) {
  844. DXASSERT(IsSizeWcharAligned(blobLen),
  845. "otherwise, UTF-16 blob size not evenly divisible by 2");
  846. utf16CharCount = blobLen / sizeof(wchar_t);
  847. if (IsBufferNullTerminated(pBlob->GetBufferPointer(), blobLen, CP_UTF16)) {
  848. // Already null-terminated, reference other blob's memory
  849. InternalDxcBlobUtf16* internalEncoding;
  850. hr = InternalDxcBlobUtf16::CreateFromBlob(pBlob, pMalloc, true, CP_UTF16, &internalEncoding);
  851. if (SUCCEEDED(hr)) {
  852. *pBlobEncoding = internalEncoding;
  853. }
  854. return hr;
  855. } else {
  856. // Copy to new buffer and null-terminate
  857. if(!utf16NewCopy.Allocate(utf16CharCount + 1))
  858. return E_OUTOFMEMORY;
  859. memcpy(utf16NewCopy.m_pData, pBlob->GetBufferPointer(), blobLen);
  860. utf16NewCopy.m_pData[utf16CharCount++] = 0;
  861. }
  862. } else {
  863. // Convert and create a blob that owns the encoding.
  864. if (FAILED(
  865. hr = CodePageBufferToUtf16(codePage, pBlob->GetBufferPointer(), blobLen,
  866. utf16NewCopy, &utf16CharCount))) {
  867. return hr;
  868. }
  869. }
  870. // At this point, we have new utf16NewCopy to wrap in a blob
  871. DXASSERT(!utf16CharCount ||
  872. IsBufferNullTerminated(utf16NewCopy.m_pData, utf16CharCount * sizeof(wchar_t), CP_UTF16),
  873. "otherwise, failed to null-terminate buffer.");
  874. InternalDxcBlobUtf16* internalEncoding;
  875. hr = InternalDxcBlobUtf16::CreateFromMalloc(
  876. utf16NewCopy.m_pData, pMalloc,
  877. utf16CharCount * sizeof(WCHAR), true, CP_UTF16, &internalEncoding);
  878. if (SUCCEEDED(hr)) {
  879. *pBlobEncoding = internalEncoding;
  880. utf16NewCopy.Detach();
  881. }
  882. return hr;
  883. }
  884. bool IsBlobNullOrEmpty(_In_opt_ IDxcBlob *pBlob) throw() {
  885. return pBlob == nullptr || pBlob->GetBufferSize() == 0;
  886. }
  887. ///////////////////////////////////////////////////////////////////////////////
  888. // Stream implementations.
  889. class MemoryStream : public AbstractMemoryStream, public IDxcBlob {
  890. private:
  891. DXC_MICROCOM_TM_REF_FIELDS()
  892. LPBYTE m_pMemory = nullptr;
  893. ULONG m_offset = 0;
  894. ULONG m_size = 0;
  895. ULONG m_allocSize = 0;
  896. public:
  897. DXC_MICROCOM_ADDREF_IMPL(m_dwRef)
  898. ULONG STDMETHODCALLTYPE Release() override {
  899. // Because memory streams are also used by tests and utilities,
  900. // we avoid using TLS.
  901. ULONG result = (ULONG)--m_dwRef;
  902. if (result == 0) {
  903. CComPtr<IMalloc> pTmp(m_pMalloc);
  904. this->~MemoryStream();
  905. pTmp->Free(this);
  906. }
  907. return result;
  908. }
  909. DXC_MICROCOM_TM_CTOR(MemoryStream)
  910. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  911. return DoBasicQueryInterface<IStream, ISequentialStream, IDxcBlob>(this, iid, ppvObject);
  912. }
  913. ~MemoryStream() {
  914. Reset();
  915. }
  916. HRESULT Grow(ULONG targetSize) {
  917. if (targetSize < m_allocSize * 2) {
  918. targetSize = m_allocSize * 2;
  919. }
  920. return Reserve(targetSize);
  921. }
  922. void Reset() {
  923. if (m_pMemory != nullptr) {
  924. m_pMalloc->Free(m_pMemory);
  925. }
  926. m_pMemory = nullptr;
  927. m_offset = 0;
  928. m_size = 0;
  929. m_allocSize = 0;
  930. }
  931. // AbstractMemoryStream implementation.
  932. LPBYTE GetPtr() throw() override {
  933. return m_pMemory;
  934. }
  935. ULONG GetPtrSize() throw() override {
  936. return m_size;
  937. }
  938. LPBYTE Detach() throw() override {
  939. LPBYTE result = m_pMemory;
  940. m_pMemory = nullptr;
  941. Reset();
  942. return result;
  943. }
  944. UINT64 GetPosition() throw() override {
  945. return m_offset;
  946. }
  947. HRESULT Reserve(ULONG targetSize) throw() override {
  948. if (m_pMemory == nullptr) {
  949. m_pMemory = (LPBYTE)m_pMalloc->Alloc(targetSize);
  950. if (m_pMemory == nullptr) {
  951. return E_OUTOFMEMORY;
  952. }
  953. }
  954. else {
  955. void* newPtr = m_pMalloc->Realloc(m_pMemory, targetSize);
  956. if (newPtr == nullptr) {
  957. return E_OUTOFMEMORY;
  958. }
  959. m_pMemory = (LPBYTE)newPtr;
  960. }
  961. m_allocSize = targetSize;
  962. return S_OK;
  963. }
  964. // IDxcBlob implementation. Requires no further writes.
  965. LPVOID STDMETHODCALLTYPE GetBufferPointer(void) override {
  966. return m_pMemory;
  967. }
  968. SIZE_T STDMETHODCALLTYPE GetBufferSize(void) override {
  969. return m_size;
  970. }
  971. // ISequentialStream implementation.
  972. HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead) override {
  973. if (!pv || !pcbRead) return E_POINTER;
  974. // If we seeked past the end, read nothing.
  975. if (m_offset > m_size) {
  976. *pcbRead = 0;
  977. return S_FALSE;
  978. }
  979. ULONG cbLeft = m_size - m_offset;
  980. *pcbRead = std::min(cb, cbLeft);
  981. memcpy(pv, m_pMemory + m_offset, *pcbRead);
  982. m_offset += *pcbRead;
  983. return (*pcbRead == cb) ? S_OK : S_FALSE;
  984. }
  985. HRESULT STDMETHODCALLTYPE Write(void const* pv, ULONG cb, ULONG* pcbWritten) override {
  986. if (!pv || !pcbWritten) return E_POINTER;
  987. if (cb + m_offset > m_allocSize) {
  988. HRESULT hr = Grow(cb + m_offset);
  989. if (FAILED(hr)) return hr;
  990. // Implicitly extend as needed with zeroes.
  991. if (m_offset > m_size) {
  992. memset(m_pMemory + m_size, 0, m_offset - m_size);
  993. }
  994. }
  995. *pcbWritten = cb;
  996. memcpy(m_pMemory + m_offset, pv, cb);
  997. m_offset += cb;
  998. m_size = std::max(m_size, m_offset);
  999. return S_OK;
  1000. }
  1001. // IStream implementation.
  1002. HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER val) override {
  1003. if (val.u.HighPart != 0) {
  1004. return E_OUTOFMEMORY;
  1005. }
  1006. if (val.u.LowPart > m_allocSize) {
  1007. return Grow(m_allocSize);
  1008. }
  1009. if (val.u.LowPart < m_size) {
  1010. m_size = val.u.LowPart;
  1011. m_offset = std::min(m_offset, m_size);
  1012. }
  1013. else if (val.u.LowPart > m_size) {
  1014. memset(m_pMemory + m_size, 0, val.u.LowPart - m_size);
  1015. m_size = val.u.LowPart;
  1016. }
  1017. return S_OK;
  1018. }
  1019. HRESULT STDMETHODCALLTYPE CopyTo(IStream *, ULARGE_INTEGER,
  1020. ULARGE_INTEGER *,
  1021. ULARGE_INTEGER *) override {
  1022. return E_NOTIMPL;
  1023. }
  1024. HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
  1025. HRESULT STDMETHODCALLTYPE Revert(void) override { return E_NOTIMPL; }
  1026. HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
  1027. ULARGE_INTEGER, DWORD) override {
  1028. return E_NOTIMPL;
  1029. }
  1030. HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
  1031. ULARGE_INTEGER, DWORD) override {
  1032. return E_NOTIMPL;
  1033. }
  1034. HRESULT STDMETHODCALLTYPE Clone(IStream **) override { return E_NOTIMPL; }
  1035. HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
  1036. DWORD dwOrigin,
  1037. ULARGE_INTEGER *lpNewFilePointer) override {
  1038. if (lpNewFilePointer != nullptr) {
  1039. lpNewFilePointer->QuadPart = 0;
  1040. }
  1041. if (liDistanceToMove.u.HighPart != 0) {
  1042. return E_FAIL;
  1043. }
  1044. ULONG targetOffset;
  1045. switch (dwOrigin) {
  1046. case STREAM_SEEK_SET:
  1047. targetOffset = liDistanceToMove.u.LowPart;
  1048. break;
  1049. case STREAM_SEEK_CUR:
  1050. targetOffset = liDistanceToMove.u.LowPart + m_offset;
  1051. break;
  1052. case STREAM_SEEK_END:
  1053. targetOffset = liDistanceToMove.u.LowPart + m_size;
  1054. break;
  1055. default:
  1056. return STG_E_INVALIDFUNCTION;
  1057. }
  1058. m_offset = targetOffset;
  1059. if (lpNewFilePointer != nullptr) {
  1060. lpNewFilePointer->u.LowPart = targetOffset;
  1061. }
  1062. return S_OK;
  1063. }
  1064. HRESULT STDMETHODCALLTYPE Stat(STATSTG *pStatstg,
  1065. DWORD grfStatFlag) override {
  1066. if (pStatstg == nullptr) {
  1067. return E_POINTER;
  1068. }
  1069. ZeroMemory(pStatstg, sizeof(*pStatstg));
  1070. pStatstg->type = STGTY_STREAM;
  1071. pStatstg->cbSize.u.LowPart = m_size;
  1072. return S_OK;
  1073. }
  1074. };
  1075. class ReadOnlyBlobStream : public IStream {
  1076. private:
  1077. DXC_MICROCOM_TM_REF_FIELDS()
  1078. CComPtr<IDxcBlob> m_pSource;
  1079. LPBYTE m_pMemory;
  1080. ULONG m_offset;
  1081. ULONG m_size;
  1082. public:
  1083. DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL()
  1084. DXC_MICROCOM_TM_CTOR(ReadOnlyBlobStream)
  1085. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  1086. return DoBasicQueryInterface<IStream, ISequentialStream>(this, iid, ppvObject);
  1087. }
  1088. void Init(IDxcBlob *pSource) {
  1089. m_pSource = pSource;
  1090. m_offset = 0;
  1091. m_size = m_pSource->GetBufferSize();
  1092. m_pMemory = (LPBYTE)m_pSource->GetBufferPointer();
  1093. // If Utf8Blob, exclude terminating null character
  1094. CComPtr<IDxcBlobUtf8> utf8Source;
  1095. if (m_size && SUCCEEDED(pSource->QueryInterface(&utf8Source)))
  1096. m_size = utf8Source->GetStringLength();
  1097. }
  1098. // ISequentialStream implementation.
  1099. HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb,
  1100. ULONG *pcbRead) override {
  1101. if (!pv || !pcbRead)
  1102. return E_POINTER;
  1103. ULONG cbLeft = m_size - m_offset;
  1104. *pcbRead = std::min(cb, cbLeft);
  1105. memcpy(pv, m_pMemory + m_offset, *pcbRead);
  1106. m_offset += *pcbRead;
  1107. return (*pcbRead == cb) ? S_OK : S_FALSE;
  1108. }
  1109. HRESULT STDMETHODCALLTYPE Write(void const *, ULONG, ULONG *) override {
  1110. return STG_E_ACCESSDENIED;
  1111. }
  1112. // IStream implementation.
  1113. HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER val) override {
  1114. return STG_E_ACCESSDENIED;
  1115. }
  1116. HRESULT STDMETHODCALLTYPE CopyTo(IStream *, ULARGE_INTEGER,
  1117. ULARGE_INTEGER *,
  1118. ULARGE_INTEGER *) override {
  1119. return E_NOTIMPL;
  1120. }
  1121. HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
  1122. HRESULT STDMETHODCALLTYPE Revert(void) override { return E_NOTIMPL; }
  1123. HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
  1124. ULARGE_INTEGER, DWORD) override {
  1125. return E_NOTIMPL;
  1126. }
  1127. HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
  1128. ULARGE_INTEGER, DWORD) override {
  1129. return E_NOTIMPL;
  1130. }
  1131. HRESULT STDMETHODCALLTYPE Clone(IStream **) override { return E_NOTIMPL; }
  1132. HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
  1133. DWORD dwOrigin,
  1134. ULARGE_INTEGER *lpNewFilePointer) override {
  1135. if (lpNewFilePointer != nullptr) {
  1136. lpNewFilePointer->QuadPart = 0;
  1137. }
  1138. if (liDistanceToMove.u.HighPart != 0) {
  1139. return E_FAIL;
  1140. }
  1141. ULONG targetOffset;
  1142. switch (dwOrigin) {
  1143. case STREAM_SEEK_SET:
  1144. targetOffset = liDistanceToMove.u.LowPart;
  1145. break;
  1146. case STREAM_SEEK_CUR:
  1147. targetOffset = liDistanceToMove.u.LowPart + m_offset;
  1148. break;
  1149. case STREAM_SEEK_END:
  1150. targetOffset = liDistanceToMove.u.LowPart + m_size;
  1151. break;
  1152. default:
  1153. return STG_E_INVALIDFUNCTION;
  1154. }
  1155. // Do not implicility extend.
  1156. if (targetOffset > m_size) {
  1157. return E_FAIL;
  1158. }
  1159. m_offset = targetOffset;
  1160. if (lpNewFilePointer != nullptr) {
  1161. lpNewFilePointer->u.LowPart = targetOffset;
  1162. }
  1163. return S_OK;
  1164. }
  1165. HRESULT STDMETHODCALLTYPE Stat(STATSTG *pStatstg,
  1166. DWORD grfStatFlag) override {
  1167. if (pStatstg == nullptr) {
  1168. return E_POINTER;
  1169. }
  1170. ZeroMemory(pStatstg, sizeof(*pStatstg));
  1171. pStatstg->type = STGTY_STREAM;
  1172. pStatstg->cbSize.u.LowPart = m_size;
  1173. return S_OK;
  1174. }
  1175. };
  1176. class FixedSizeMemoryStream : public AbstractMemoryStream {
  1177. private:
  1178. DXC_MICROCOM_TM_REF_FIELDS()
  1179. LPBYTE m_pBuffer;
  1180. ULONG m_offset;
  1181. ULONG m_size;
  1182. public:
  1183. DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL()
  1184. DXC_MICROCOM_TM_CTOR(FixedSizeMemoryStream)
  1185. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  1186. return DoBasicQueryInterface<IStream, ISequentialStream>(this, iid, ppvObject);
  1187. }
  1188. void Init(LPBYTE pBuffer, size_t size) {
  1189. m_pBuffer = pBuffer;
  1190. m_offset = 0;
  1191. m_size = size;
  1192. }
  1193. // ISequentialStream implementation.
  1194. HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead) override {
  1195. if (!pv || !pcbRead)
  1196. return E_POINTER;
  1197. ULONG cbLeft = m_size - m_offset;
  1198. *pcbRead = std::min(cb, cbLeft);
  1199. memcpy(pv, m_pBuffer + m_offset, *pcbRead);
  1200. m_offset += *pcbRead;
  1201. return (*pcbRead == cb) ? S_OK : S_FALSE;
  1202. }
  1203. HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten) override {
  1204. if (!pv || !pcbWritten)
  1205. return E_POINTER;
  1206. ULONG cbLeft = m_size - m_offset;
  1207. *pcbWritten = std::min(cb, cbLeft);
  1208. memcpy(m_pBuffer + m_offset, pv, *pcbWritten);
  1209. m_offset += *pcbWritten;
  1210. return (*pcbWritten == cb) ? S_OK : S_FALSE;
  1211. }
  1212. // IStream implementation.
  1213. HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER val) override {
  1214. return STG_E_ACCESSDENIED;
  1215. }
  1216. HRESULT STDMETHODCALLTYPE CopyTo(IStream *, ULARGE_INTEGER,
  1217. ULARGE_INTEGER *, ULARGE_INTEGER *) override {
  1218. return E_NOTIMPL;
  1219. }
  1220. HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
  1221. HRESULT STDMETHODCALLTYPE Revert(void) override { return E_NOTIMPL; }
  1222. HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
  1223. ULARGE_INTEGER, DWORD) override {
  1224. return E_NOTIMPL;
  1225. }
  1226. HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
  1227. ULARGE_INTEGER, DWORD) override {
  1228. return E_NOTIMPL;
  1229. }
  1230. HRESULT STDMETHODCALLTYPE Clone(IStream **) override { return E_NOTIMPL; }
  1231. HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER, DWORD, ULARGE_INTEGER *) override {
  1232. return E_NOTIMPL;
  1233. }
  1234. HRESULT STDMETHODCALLTYPE Stat(STATSTG *pStatstg,
  1235. DWORD grfStatFlag) override {
  1236. if (pStatstg == nullptr) {
  1237. return E_POINTER;
  1238. }
  1239. ZeroMemory(pStatstg, sizeof(*pStatstg));
  1240. pStatstg->type = STGTY_STREAM;
  1241. pStatstg->cbSize.u.LowPart = m_size;
  1242. return S_OK;
  1243. }
  1244. // AbstractMemoryStream implementation
  1245. LPBYTE GetPtr() throw() override {
  1246. return m_pBuffer;
  1247. }
  1248. ULONG GetPtrSize() throw() override {
  1249. return m_size;
  1250. }
  1251. LPBYTE Detach() throw() override {
  1252. LPBYTE result = m_pBuffer;
  1253. m_pBuffer = nullptr;
  1254. m_size = 0;
  1255. m_offset = 0;
  1256. return result;
  1257. }
  1258. UINT64 GetPosition() throw() override {
  1259. return m_offset;
  1260. }
  1261. HRESULT Reserve(ULONG targetSize) throw() override {
  1262. return targetSize <= m_size ? S_OK : E_BOUNDS;
  1263. }
  1264. };
  1265. HRESULT CreateMemoryStream(_In_ IMalloc *pMalloc, _COM_Outptr_ AbstractMemoryStream** ppResult) throw() {
  1266. if (pMalloc == nullptr || ppResult == nullptr) {
  1267. return E_POINTER;
  1268. }
  1269. CComPtr<MemoryStream> stream = MemoryStream::Alloc(pMalloc);
  1270. *ppResult = stream.Detach();
  1271. return (*ppResult == nullptr) ? E_OUTOFMEMORY : S_OK;
  1272. }
  1273. HRESULT CreateReadOnlyBlobStream(_In_ IDxcBlob *pSource, _COM_Outptr_ IStream** ppResult) throw() {
  1274. if (pSource == nullptr || ppResult == nullptr) {
  1275. return E_POINTER;
  1276. }
  1277. CComPtr<ReadOnlyBlobStream> stream = ReadOnlyBlobStream::Alloc(DxcGetThreadMallocNoRef());
  1278. if (stream.p) {
  1279. stream->Init(pSource);
  1280. }
  1281. *ppResult = stream.Detach();
  1282. return (*ppResult == nullptr) ? E_OUTOFMEMORY : S_OK;
  1283. }
  1284. HRESULT CreateFixedSizeMemoryStream(_In_ LPBYTE pBuffer, size_t size, _COM_Outptr_ AbstractMemoryStream** ppResult) throw() {
  1285. if (pBuffer == nullptr || ppResult == nullptr) {
  1286. return E_POINTER;
  1287. }
  1288. CComPtr<FixedSizeMemoryStream> stream = FixedSizeMemoryStream::Alloc(DxcGetThreadMallocNoRef());
  1289. if (stream.p) {
  1290. stream->Init(pBuffer, size);
  1291. }
  1292. *ppResult = stream.Detach();
  1293. return (*ppResult == nullptr) ? E_OUTOFMEMORY : S_OK;
  1294. }
  1295. } // namespace hlsl