FileIOHelper.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479
  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. static bool IsSizeWcharAligned(SIZE_T size) { return (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. _Use_decl_annotations_
  693. HRESULT
  694. DxcCreateBlobWithEncodingFromStream(IStream *pStream, bool newInstanceAlways,
  695. UINT32 codePage,
  696. IDxcBlobEncoding **ppBlobEncoding) throw() {
  697. IFRBOOL(ppBlobEncoding, E_POINTER);
  698. *ppBlobEncoding = nullptr;
  699. if (pStream == nullptr) {
  700. return S_OK;
  701. }
  702. // Try to reuse the existing stream.
  703. if (!newInstanceAlways) {
  704. CComPtr<IDxcBlobEncoding> blobEncoding;
  705. if (SUCCEEDED(pStream->QueryInterface(&blobEncoding))) {
  706. *ppBlobEncoding = blobEncoding.Detach();
  707. return S_OK;
  708. }
  709. }
  710. // Layer over the blob if possible.
  711. CComPtr<IDxcBlob> blob;
  712. if (SUCCEEDED(pStream->QueryInterface(&blob))) {
  713. return DxcCreateBlobWithEncodingSet(blob, codePage, ppBlobEncoding);
  714. }
  715. // Create a copy of contents, last resort.
  716. // TODO: implement when we find this codepath internally
  717. return E_NOTIMPL;
  718. }
  719. _Use_decl_annotations_
  720. HRESULT
  721. DxcCreateBlobWithEncodingOnHeapCopy(LPCVOID pText, UINT32 size, UINT32 codePage,
  722. IDxcBlobEncoding **pBlobEncoding) throw() {
  723. return DxcCreateBlob(pText, size, false, true, true, codePage, nullptr, pBlobEncoding);
  724. }
  725. _Use_decl_annotations_
  726. HRESULT
  727. DxcCreateBlobWithEncodingOnMalloc(LPCVOID pText, IMalloc *pIMalloc, UINT32 size, UINT32 codePage,
  728. IDxcBlobEncoding **pBlobEncoding) throw() {
  729. return DxcCreateBlob(pText, size, false, false, true, codePage, pIMalloc, pBlobEncoding);
  730. }
  731. _Use_decl_annotations_
  732. HRESULT
  733. DxcCreateBlobWithEncodingOnMallocCopy(IMalloc *pIMalloc, LPCVOID pText, UINT32 size, UINT32 codePage,
  734. IDxcBlobEncoding **ppBlobEncoding) throw() {
  735. return DxcCreateBlob(pText, size, false, true, true, codePage, pIMalloc, ppBlobEncoding);
  736. }
  737. _Use_decl_annotations_
  738. HRESULT DxcGetBlobAsUtf8(IDxcBlob *pBlob, IMalloc *pMalloc, IDxcBlobUtf8 **pBlobEncoding) throw() {
  739. IFRBOOL(pBlob, E_POINTER);
  740. IFRBOOL(pBlobEncoding, E_POINTER);
  741. *pBlobEncoding = nullptr;
  742. if (SUCCEEDED(pBlob->QueryInterface(pBlobEncoding)))
  743. return S_OK;
  744. HRESULT hr;
  745. CComPtr<IDxcBlobEncoding> pSourceBlob;
  746. UINT32 codePage = CP_ACP;
  747. BOOL known = FALSE;
  748. if (SUCCEEDED(pBlob->QueryInterface(&pSourceBlob))) {
  749. if (FAILED(hr = pSourceBlob->GetEncoding(&known, &codePage)))
  750. return hr;
  751. }
  752. SIZE_T blobLen = pBlob->GetBufferSize();
  753. if (!known) {
  754. codePage = DxcCodePageFromBytes((char *)pBlob->GetBufferPointer(), blobLen);
  755. }
  756. if (!pMalloc)
  757. pMalloc = DxcGetThreadMallocNoRef();
  758. CDxcMallocHeapPtr<char> utf8NewCopy(pMalloc);
  759. UINT32 utf8CharCount = 0;
  760. // Reuse or copy the underlying blob depending on null-termination
  761. if (codePage == CP_UTF8) {
  762. utf8CharCount = blobLen;
  763. if (IsBufferNullTerminated(pBlob->GetBufferPointer(), blobLen, CP_UTF8)) {
  764. // Already null-terminated, reference other blob's memory
  765. InternalDxcBlobUtf8* internalEncoding;
  766. hr = InternalDxcBlobUtf8::CreateFromBlob(pBlob, pMalloc, true, CP_UTF8, &internalEncoding);
  767. if (SUCCEEDED(hr)) {
  768. *pBlobEncoding = internalEncoding;
  769. }
  770. return hr;
  771. } else {
  772. // Copy to new buffer and null-terminate
  773. if(!utf8NewCopy.Allocate(utf8CharCount + 1))
  774. return E_OUTOFMEMORY;
  775. memcpy(utf8NewCopy.m_pData, pBlob->GetBufferPointer(), blobLen);
  776. utf8NewCopy.m_pData[utf8CharCount++] = 0;
  777. }
  778. } else {
  779. // Convert and create a blob that owns the encoding.
  780. if (FAILED(
  781. hr = CodePageBufferToUtf8(codePage, pBlob->GetBufferPointer(), blobLen,
  782. pMalloc, utf8NewCopy, &utf8CharCount))) {
  783. return hr;
  784. }
  785. DXASSERT(!utf8CharCount ||
  786. IsBufferNullTerminated(utf8NewCopy.m_pData, utf8CharCount, CP_UTF8),
  787. "otherwise, CodePageBufferToUtf8 failed to null-terminate buffer.");
  788. }
  789. // At this point, we have new utf8NewCopy to wrap in a blob
  790. InternalDxcBlobUtf8* internalEncoding;
  791. hr = InternalDxcBlobUtf8::CreateFromMalloc(
  792. utf8NewCopy.m_pData, pMalloc,
  793. utf8CharCount, true, CP_UTF8, &internalEncoding);
  794. if (SUCCEEDED(hr)) {
  795. *pBlobEncoding = internalEncoding;
  796. utf8NewCopy.Detach();
  797. }
  798. return hr;
  799. }
  800. // This is kept for compatibility.
  801. HRESULT
  802. DxcGetBlobAsUtf8NullTerm(_In_ IDxcBlob *pBlob,
  803. _COM_Outptr_ IDxcBlobEncoding **ppBlobEncoding) throw() {
  804. IFRBOOL(pBlob, E_POINTER);
  805. IFRBOOL(ppBlobEncoding, E_POINTER);
  806. *ppBlobEncoding = nullptr;
  807. CComPtr<IDxcBlobUtf8> pConverted;
  808. IFR(DxcGetBlobAsUtf8(pBlob, DxcGetThreadMallocNoRef(), &pConverted));
  809. pConverted->QueryInterface(ppBlobEncoding);
  810. return S_OK;
  811. }
  812. _Use_decl_annotations_
  813. HRESULT DxcGetBlobAsUtf16(IDxcBlob *pBlob, IMalloc *pMalloc, IDxcBlobUtf16 **pBlobEncoding) throw() {
  814. IFRBOOL(pBlob, E_POINTER);
  815. IFRBOOL(pBlobEncoding, E_POINTER);
  816. *pBlobEncoding = nullptr;
  817. if (SUCCEEDED(pBlob->QueryInterface(pBlobEncoding)))
  818. return S_OK;
  819. HRESULT hr;
  820. CComPtr<IDxcBlobEncoding> pSourceBlob;
  821. UINT32 codePage = CP_ACP;
  822. BOOL known = FALSE;
  823. if (SUCCEEDED(pBlob->QueryInterface(&pSourceBlob))) {
  824. if (FAILED(hr = pSourceBlob->GetEncoding(&known, &codePage)))
  825. return hr;
  826. }
  827. SIZE_T blobLen = pBlob->GetBufferSize();
  828. if (!known) {
  829. codePage = DxcCodePageFromBytes((char *)pBlob->GetBufferPointer(), blobLen);
  830. }
  831. if (!pMalloc)
  832. pMalloc = DxcGetThreadMallocNoRef();
  833. CDxcMallocHeapPtr<WCHAR> utf16NewCopy(pMalloc);
  834. UINT32 utf16CharCount = 0;
  835. // Reuse or copy the underlying blob depending on null-termination
  836. if (codePage == CP_UTF16) {
  837. DXASSERT(IsSizeWcharAligned(blobLen),
  838. "otherwise, UTF-16 blob size not evenly divisible by 2");
  839. utf16CharCount = blobLen / sizeof(wchar_t);
  840. if (IsBufferNullTerminated(pBlob->GetBufferPointer(), blobLen, CP_UTF16)) {
  841. // Already null-terminated, reference other blob's memory
  842. InternalDxcBlobUtf16* internalEncoding;
  843. hr = InternalDxcBlobUtf16::CreateFromBlob(pBlob, pMalloc, true, CP_UTF16, &internalEncoding);
  844. if (SUCCEEDED(hr)) {
  845. *pBlobEncoding = internalEncoding;
  846. }
  847. return hr;
  848. } else {
  849. // Copy to new buffer and null-terminate
  850. if(!utf16NewCopy.Allocate(utf16CharCount + 1))
  851. return E_OUTOFMEMORY;
  852. memcpy(utf16NewCopy.m_pData, pBlob->GetBufferPointer(), blobLen);
  853. utf16NewCopy.m_pData[utf16CharCount++] = 0;
  854. }
  855. } else {
  856. // Convert and create a blob that owns the encoding.
  857. if (FAILED(
  858. hr = CodePageBufferToUtf16(codePage, pBlob->GetBufferPointer(), blobLen,
  859. utf16NewCopy, &utf16CharCount))) {
  860. return hr;
  861. }
  862. }
  863. // At this point, we have new utf16NewCopy to wrap in a blob
  864. DXASSERT(!utf16CharCount ||
  865. IsBufferNullTerminated(utf16NewCopy.m_pData, utf16CharCount * sizeof(wchar_t), CP_UTF16),
  866. "otherwise, failed to null-terminate buffer.");
  867. InternalDxcBlobUtf16* internalEncoding;
  868. hr = InternalDxcBlobUtf16::CreateFromMalloc(
  869. utf16NewCopy.m_pData, pMalloc,
  870. utf16CharCount * sizeof(WCHAR), true, CP_UTF16, &internalEncoding);
  871. if (SUCCEEDED(hr)) {
  872. *pBlobEncoding = internalEncoding;
  873. utf16NewCopy.Detach();
  874. }
  875. return hr;
  876. }
  877. bool IsBlobNullOrEmpty(_In_opt_ IDxcBlob *pBlob) throw() {
  878. return pBlob == nullptr || pBlob->GetBufferSize() == 0;
  879. }
  880. ///////////////////////////////////////////////////////////////////////////////
  881. // Stream implementations.
  882. class MemoryStream : public AbstractMemoryStream, public IDxcBlob {
  883. private:
  884. DXC_MICROCOM_TM_REF_FIELDS()
  885. LPBYTE m_pMemory = nullptr;
  886. ULONG m_offset = 0;
  887. ULONG m_size = 0;
  888. ULONG m_allocSize = 0;
  889. public:
  890. DXC_MICROCOM_ADDREF_IMPL(m_dwRef)
  891. ULONG STDMETHODCALLTYPE Release() override {
  892. // Because memory streams are also used by tests and utilities,
  893. // we avoid using TLS.
  894. ULONG result = (ULONG)--m_dwRef;
  895. if (result == 0) {
  896. CComPtr<IMalloc> pTmp(m_pMalloc);
  897. this->~MemoryStream();
  898. pTmp->Free(this);
  899. }
  900. return result;
  901. }
  902. DXC_MICROCOM_TM_CTOR(MemoryStream)
  903. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  904. return DoBasicQueryInterface<IStream, ISequentialStream, IDxcBlob>(this, iid, ppvObject);
  905. }
  906. ~MemoryStream() {
  907. Reset();
  908. }
  909. HRESULT Grow(ULONG targetSize) {
  910. if (targetSize < m_allocSize * 2) {
  911. targetSize = m_allocSize * 2;
  912. }
  913. return Reserve(targetSize);
  914. }
  915. void Reset() {
  916. if (m_pMemory != nullptr) {
  917. m_pMalloc->Free(m_pMemory);
  918. }
  919. m_pMemory = nullptr;
  920. m_offset = 0;
  921. m_size = 0;
  922. m_allocSize = 0;
  923. }
  924. // AbstractMemoryStream implementation.
  925. LPBYTE GetPtr() throw() override {
  926. return m_pMemory;
  927. }
  928. ULONG GetPtrSize() throw() override {
  929. return m_size;
  930. }
  931. LPBYTE Detach() throw() override {
  932. LPBYTE result = m_pMemory;
  933. m_pMemory = nullptr;
  934. Reset();
  935. return result;
  936. }
  937. UINT64 GetPosition() throw() override {
  938. return m_offset;
  939. }
  940. HRESULT Reserve(ULONG targetSize) throw() override {
  941. if (m_pMemory == nullptr) {
  942. m_pMemory = (LPBYTE)m_pMalloc->Alloc(targetSize);
  943. if (m_pMemory == nullptr) {
  944. return E_OUTOFMEMORY;
  945. }
  946. }
  947. else {
  948. void* newPtr = m_pMalloc->Realloc(m_pMemory, targetSize);
  949. if (newPtr == nullptr) {
  950. return E_OUTOFMEMORY;
  951. }
  952. m_pMemory = (LPBYTE)newPtr;
  953. }
  954. m_allocSize = targetSize;
  955. return S_OK;
  956. }
  957. // IDxcBlob implementation. Requires no further writes.
  958. LPVOID STDMETHODCALLTYPE GetBufferPointer(void) override {
  959. return m_pMemory;
  960. }
  961. SIZE_T STDMETHODCALLTYPE GetBufferSize(void) override {
  962. return m_size;
  963. }
  964. // ISequentialStream implementation.
  965. HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead) override {
  966. if (!pv || !pcbRead) return E_POINTER;
  967. // If we seeked past the end, read nothing.
  968. if (m_offset > m_size) {
  969. *pcbRead = 0;
  970. return S_FALSE;
  971. }
  972. ULONG cbLeft = m_size - m_offset;
  973. *pcbRead = std::min(cb, cbLeft);
  974. memcpy(pv, m_pMemory + m_offset, *pcbRead);
  975. m_offset += *pcbRead;
  976. return (*pcbRead == cb) ? S_OK : S_FALSE;
  977. }
  978. HRESULT STDMETHODCALLTYPE Write(void const* pv, ULONG cb, ULONG* pcbWritten) override {
  979. if (!pv || !pcbWritten) return E_POINTER;
  980. if (cb + m_offset > m_allocSize) {
  981. HRESULT hr = Grow(cb + m_offset);
  982. if (FAILED(hr)) return hr;
  983. // Implicitly extend as needed with zeroes.
  984. if (m_offset > m_size) {
  985. memset(m_pMemory + m_size, 0, m_offset - m_size);
  986. }
  987. }
  988. *pcbWritten = cb;
  989. memcpy(m_pMemory + m_offset, pv, cb);
  990. m_offset += cb;
  991. m_size = std::max(m_size, m_offset);
  992. return S_OK;
  993. }
  994. // IStream implementation.
  995. HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER val) override {
  996. if (val.u.HighPart != 0) {
  997. return E_OUTOFMEMORY;
  998. }
  999. if (val.u.LowPart > m_allocSize) {
  1000. return Grow(m_allocSize);
  1001. }
  1002. if (val.u.LowPart < m_size) {
  1003. m_size = val.u.LowPart;
  1004. m_offset = std::min(m_offset, m_size);
  1005. }
  1006. else if (val.u.LowPart > m_size) {
  1007. memset(m_pMemory + m_size, 0, val.u.LowPart - m_size);
  1008. m_size = val.u.LowPart;
  1009. }
  1010. return S_OK;
  1011. }
  1012. HRESULT STDMETHODCALLTYPE CopyTo(IStream *, ULARGE_INTEGER,
  1013. ULARGE_INTEGER *,
  1014. ULARGE_INTEGER *) override {
  1015. return E_NOTIMPL;
  1016. }
  1017. HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
  1018. HRESULT STDMETHODCALLTYPE Revert(void) override { return E_NOTIMPL; }
  1019. HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
  1020. ULARGE_INTEGER, DWORD) override {
  1021. return E_NOTIMPL;
  1022. }
  1023. HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
  1024. ULARGE_INTEGER, DWORD) override {
  1025. return E_NOTIMPL;
  1026. }
  1027. HRESULT STDMETHODCALLTYPE Clone(IStream **) override { return E_NOTIMPL; }
  1028. HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
  1029. DWORD dwOrigin,
  1030. ULARGE_INTEGER *lpNewFilePointer) override {
  1031. if (lpNewFilePointer != nullptr) {
  1032. lpNewFilePointer->QuadPart = 0;
  1033. }
  1034. if (liDistanceToMove.u.HighPart != 0) {
  1035. return E_FAIL;
  1036. }
  1037. ULONG targetOffset;
  1038. switch (dwOrigin) {
  1039. case STREAM_SEEK_SET:
  1040. targetOffset = liDistanceToMove.u.LowPart;
  1041. break;
  1042. case STREAM_SEEK_CUR:
  1043. targetOffset = liDistanceToMove.u.LowPart + m_offset;
  1044. break;
  1045. case STREAM_SEEK_END:
  1046. targetOffset = liDistanceToMove.u.LowPart + m_size;
  1047. break;
  1048. default:
  1049. return STG_E_INVALIDFUNCTION;
  1050. }
  1051. m_offset = targetOffset;
  1052. if (lpNewFilePointer != nullptr) {
  1053. lpNewFilePointer->u.LowPart = targetOffset;
  1054. }
  1055. return S_OK;
  1056. }
  1057. HRESULT STDMETHODCALLTYPE Stat(STATSTG *pStatstg,
  1058. DWORD grfStatFlag) override {
  1059. if (pStatstg == nullptr) {
  1060. return E_POINTER;
  1061. }
  1062. ZeroMemory(pStatstg, sizeof(*pStatstg));
  1063. pStatstg->type = STGTY_STREAM;
  1064. pStatstg->cbSize.u.LowPart = m_size;
  1065. return S_OK;
  1066. }
  1067. };
  1068. class ReadOnlyBlobStream : public IStream {
  1069. private:
  1070. DXC_MICROCOM_TM_REF_FIELDS()
  1071. CComPtr<IDxcBlob> m_pSource;
  1072. LPBYTE m_pMemory;
  1073. ULONG m_offset;
  1074. ULONG m_size;
  1075. public:
  1076. DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL()
  1077. DXC_MICROCOM_TM_CTOR(ReadOnlyBlobStream)
  1078. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  1079. return DoBasicQueryInterface<IStream, ISequentialStream>(this, iid, ppvObject);
  1080. }
  1081. void Init(IDxcBlob *pSource) {
  1082. m_pSource = pSource;
  1083. m_offset = 0;
  1084. m_size = m_pSource->GetBufferSize();
  1085. m_pMemory = (LPBYTE)m_pSource->GetBufferPointer();
  1086. // If Utf8Blob, exclude terminating null character
  1087. CComPtr<IDxcBlobUtf8> utf8Source;
  1088. if (m_size && SUCCEEDED(pSource->QueryInterface(&utf8Source)))
  1089. m_size = utf8Source->GetStringLength();
  1090. }
  1091. // ISequentialStream implementation.
  1092. HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb,
  1093. ULONG *pcbRead) override {
  1094. if (!pv || !pcbRead)
  1095. return E_POINTER;
  1096. ULONG cbLeft = m_size - m_offset;
  1097. *pcbRead = std::min(cb, cbLeft);
  1098. memcpy(pv, m_pMemory + m_offset, *pcbRead);
  1099. m_offset += *pcbRead;
  1100. return (*pcbRead == cb) ? S_OK : S_FALSE;
  1101. }
  1102. HRESULT STDMETHODCALLTYPE Write(void const *, ULONG, ULONG *) override {
  1103. return STG_E_ACCESSDENIED;
  1104. }
  1105. // IStream implementation.
  1106. HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER val) override {
  1107. return STG_E_ACCESSDENIED;
  1108. }
  1109. HRESULT STDMETHODCALLTYPE CopyTo(IStream *, ULARGE_INTEGER,
  1110. ULARGE_INTEGER *,
  1111. ULARGE_INTEGER *) override {
  1112. return E_NOTIMPL;
  1113. }
  1114. HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
  1115. HRESULT STDMETHODCALLTYPE Revert(void) override { return E_NOTIMPL; }
  1116. HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
  1117. ULARGE_INTEGER, DWORD) override {
  1118. return E_NOTIMPL;
  1119. }
  1120. HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
  1121. ULARGE_INTEGER, DWORD) override {
  1122. return E_NOTIMPL;
  1123. }
  1124. HRESULT STDMETHODCALLTYPE Clone(IStream **) override { return E_NOTIMPL; }
  1125. HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
  1126. DWORD dwOrigin,
  1127. ULARGE_INTEGER *lpNewFilePointer) override {
  1128. if (lpNewFilePointer != nullptr) {
  1129. lpNewFilePointer->QuadPart = 0;
  1130. }
  1131. if (liDistanceToMove.u.HighPart != 0) {
  1132. return E_FAIL;
  1133. }
  1134. ULONG targetOffset;
  1135. switch (dwOrigin) {
  1136. case STREAM_SEEK_SET:
  1137. targetOffset = liDistanceToMove.u.LowPart;
  1138. break;
  1139. case STREAM_SEEK_CUR:
  1140. targetOffset = liDistanceToMove.u.LowPart + m_offset;
  1141. break;
  1142. case STREAM_SEEK_END:
  1143. targetOffset = liDistanceToMove.u.LowPart + m_size;
  1144. break;
  1145. default:
  1146. return STG_E_INVALIDFUNCTION;
  1147. }
  1148. // Do not implicility extend.
  1149. if (targetOffset > m_size) {
  1150. return E_FAIL;
  1151. }
  1152. m_offset = targetOffset;
  1153. if (lpNewFilePointer != nullptr) {
  1154. lpNewFilePointer->u.LowPart = targetOffset;
  1155. }
  1156. return S_OK;
  1157. }
  1158. HRESULT STDMETHODCALLTYPE Stat(STATSTG *pStatstg,
  1159. DWORD grfStatFlag) override {
  1160. if (pStatstg == nullptr) {
  1161. return E_POINTER;
  1162. }
  1163. ZeroMemory(pStatstg, sizeof(*pStatstg));
  1164. pStatstg->type = STGTY_STREAM;
  1165. pStatstg->cbSize.u.LowPart = m_size;
  1166. return S_OK;
  1167. }
  1168. };
  1169. class FixedSizeMemoryStream : public AbstractMemoryStream {
  1170. private:
  1171. DXC_MICROCOM_TM_REF_FIELDS()
  1172. LPBYTE m_pBuffer;
  1173. ULONG m_offset;
  1174. ULONG m_size;
  1175. public:
  1176. DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL()
  1177. DXC_MICROCOM_TM_CTOR(FixedSizeMemoryStream)
  1178. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  1179. return DoBasicQueryInterface<IStream, ISequentialStream>(this, iid, ppvObject);
  1180. }
  1181. void Init(LPBYTE pBuffer, size_t size) {
  1182. m_pBuffer = pBuffer;
  1183. m_offset = 0;
  1184. m_size = size;
  1185. }
  1186. // ISequentialStream implementation.
  1187. HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead) override {
  1188. if (!pv || !pcbRead)
  1189. return E_POINTER;
  1190. ULONG cbLeft = m_size - m_offset;
  1191. *pcbRead = std::min(cb, cbLeft);
  1192. memcpy(pv, m_pBuffer + m_offset, *pcbRead);
  1193. m_offset += *pcbRead;
  1194. return (*pcbRead == cb) ? S_OK : S_FALSE;
  1195. }
  1196. HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten) override {
  1197. if (!pv || !pcbWritten)
  1198. return E_POINTER;
  1199. ULONG cbLeft = m_size - m_offset;
  1200. *pcbWritten = std::min(cb, cbLeft);
  1201. memcpy(m_pBuffer + m_offset, pv, *pcbWritten);
  1202. m_offset += *pcbWritten;
  1203. return (*pcbWritten == cb) ? S_OK : S_FALSE;
  1204. }
  1205. // IStream implementation.
  1206. HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER val) override {
  1207. return STG_E_ACCESSDENIED;
  1208. }
  1209. HRESULT STDMETHODCALLTYPE CopyTo(IStream *, ULARGE_INTEGER,
  1210. ULARGE_INTEGER *, ULARGE_INTEGER *) override {
  1211. return E_NOTIMPL;
  1212. }
  1213. HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
  1214. HRESULT STDMETHODCALLTYPE Revert(void) override { return E_NOTIMPL; }
  1215. HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
  1216. ULARGE_INTEGER, DWORD) override {
  1217. return E_NOTIMPL;
  1218. }
  1219. HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
  1220. ULARGE_INTEGER, DWORD) override {
  1221. return E_NOTIMPL;
  1222. }
  1223. HRESULT STDMETHODCALLTYPE Clone(IStream **) override { return E_NOTIMPL; }
  1224. HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER, DWORD, ULARGE_INTEGER *) override {
  1225. return E_NOTIMPL;
  1226. }
  1227. HRESULT STDMETHODCALLTYPE Stat(STATSTG *pStatstg,
  1228. DWORD grfStatFlag) override {
  1229. if (pStatstg == nullptr) {
  1230. return E_POINTER;
  1231. }
  1232. ZeroMemory(pStatstg, sizeof(*pStatstg));
  1233. pStatstg->type = STGTY_STREAM;
  1234. pStatstg->cbSize.u.LowPart = m_size;
  1235. return S_OK;
  1236. }
  1237. // AbstractMemoryStream implementation
  1238. LPBYTE GetPtr() throw() override {
  1239. return m_pBuffer;
  1240. }
  1241. ULONG GetPtrSize() throw() override {
  1242. return m_size;
  1243. }
  1244. LPBYTE Detach() throw() override {
  1245. LPBYTE result = m_pBuffer;
  1246. m_pBuffer = nullptr;
  1247. m_size = 0;
  1248. m_offset = 0;
  1249. return result;
  1250. }
  1251. UINT64 GetPosition() throw() override {
  1252. return m_offset;
  1253. }
  1254. HRESULT Reserve(ULONG targetSize) throw() override {
  1255. return targetSize <= m_size ? S_OK : E_BOUNDS;
  1256. }
  1257. };
  1258. HRESULT CreateMemoryStream(_In_ IMalloc *pMalloc, _COM_Outptr_ AbstractMemoryStream** ppResult) throw() {
  1259. if (pMalloc == nullptr || ppResult == nullptr) {
  1260. return E_POINTER;
  1261. }
  1262. CComPtr<MemoryStream> stream = MemoryStream::Alloc(pMalloc);
  1263. *ppResult = stream.Detach();
  1264. return (*ppResult == nullptr) ? E_OUTOFMEMORY : S_OK;
  1265. }
  1266. HRESULT CreateReadOnlyBlobStream(_In_ IDxcBlob *pSource, _COM_Outptr_ IStream** ppResult) throw() {
  1267. if (pSource == nullptr || ppResult == nullptr) {
  1268. return E_POINTER;
  1269. }
  1270. CComPtr<ReadOnlyBlobStream> stream = ReadOnlyBlobStream::Alloc(DxcGetThreadMallocNoRef());
  1271. if (stream.p) {
  1272. stream->Init(pSource);
  1273. }
  1274. *ppResult = stream.Detach();
  1275. return (*ppResult == nullptr) ? E_OUTOFMEMORY : S_OK;
  1276. }
  1277. HRESULT CreateFixedSizeMemoryStream(_In_ LPBYTE pBuffer, size_t size, _COM_Outptr_ AbstractMemoryStream** ppResult) throw() {
  1278. if (pBuffer == nullptr || ppResult == nullptr) {
  1279. return E_POINTER;
  1280. }
  1281. CComPtr<FixedSizeMemoryStream> stream = FixedSizeMemoryStream::Alloc(DxcGetThreadMallocNoRef());
  1282. if (stream.p) {
  1283. stream->Init(pBuffer, size);
  1284. }
  1285. *ppResult = stream.Detach();
  1286. return (*ppResult == nullptr) ? E_OUTOFMEMORY : S_OK;
  1287. }
  1288. } // namespace hlsl