UriHelper.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. namespace Js
  7. {
  8. Var UriHelper::EncodeCoreURI(ScriptContext* scriptContext, Arguments& args, unsigned char flags )
  9. {
  10. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  11. JavascriptString * strURI;
  12. //TODO make sure this string is pinned when the memory recycler is in
  13. if(args.Info.Count < 2)
  14. {
  15. strURI = scriptContext->GetLibrary()->GetUndefinedDisplayString();
  16. }
  17. else
  18. {
  19. if (VarIs<JavascriptString>(args[1]))
  20. {
  21. strURI = VarTo<JavascriptString>(args[1]);
  22. }
  23. else
  24. {
  25. strURI = JavascriptConversion::ToString(args[1], scriptContext);
  26. }
  27. }
  28. return Encode(strURI, flags, scriptContext);
  29. }
  30. unsigned char UriHelper::s_uriProps[128] =
  31. {
  32. //0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
  33. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  34. //0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f
  35. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  36. // ! " # $ % & ' ( ) * + , - . /
  37. 0, 0x02, 0, 0x01, 0x01, 0, 0x01, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x02, 0x02, 0x01,
  38. // 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
  39. 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0, 0x01, 0, 0x01,
  40. // @ A B C D E F G H I J K L M N O
  41. 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
  42. // P Q R S T U V W X Y Z [ \ ] ^ _
  43. 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0, 0, 0, 0, 0x02,
  44. // ` a b c d e f g h i j k l m n o
  45. 0, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
  46. // p q r s t u v w x y z { | } ~ 0x7f
  47. 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0, 0, 0, 0x02, 0,
  48. };
  49. // Convert 'uVal' to it's UTF-8 encoding in the array 'bUTF8'. Returns
  50. // the number of characters in the output array.
  51. // This routine assumes that it's input 'uVal' is a valid Unicode code-point value
  52. // and does no error checking.
  53. uint32 UriHelper::ToUTF8( uint32 uVal, BYTE bUTF8[MaxUTF8Len])
  54. {
  55. uint32 uRet;
  56. if( uVal <= 0x007F )
  57. {
  58. bUTF8[0] = (BYTE)uVal;
  59. uRet = 1;
  60. }
  61. else if( uVal <= 0x07FF )
  62. {
  63. uint32 z = uVal & 0x3F;
  64. uint32 y = uVal >> 6;
  65. bUTF8[0] = (BYTE) (0xC0 | y);
  66. bUTF8[1] = (BYTE) (0x80 | z);
  67. uRet = 2;
  68. }
  69. else if( uVal <= 0xFFFF )
  70. {
  71. Assert( uVal <= 0xD7FF || uVal >= 0xE000 );
  72. uint32 z = uVal & 0x3F;
  73. uint32 y = (uVal >> 6) & 0x3F;
  74. uint32 x = (uVal >> 12);
  75. bUTF8[0] = (BYTE) (0xE0 | x);
  76. bUTF8[1] = (BYTE) (0x80 | y);
  77. bUTF8[2] = (BYTE) (0x80 | z);
  78. uRet = 3;
  79. }
  80. else
  81. {
  82. uint32 z = uVal & 0x3F;
  83. uint32 y = (uVal >> 6) &0x3F;
  84. uint32 x = (uVal >> 12) &0x3F;
  85. uint32 w = (uVal >> 18);
  86. bUTF8[0] = (BYTE) (0xF0 | w);
  87. bUTF8[1] = (BYTE) (0x80 | x);
  88. bUTF8[2] = (BYTE) (0x80 | y);
  89. bUTF8[3] = (BYTE) (0x80 | z);
  90. uRet = 4;
  91. }
  92. return uRet;
  93. }
  94. // Return the Unicode code-point value of the UTF-8 encoding passed in as the
  95. // array 'bUTF8'. uLen is the number of characters in the UTF-8 encoding.
  96. // This routine assumes that a valid UTF-8 encoding of a character is passed in
  97. // and does no error checking.
  98. uint32 UriHelper::FromUTF8( BYTE bUTF8[MaxUTF8Len], uint32 uLen )
  99. {
  100. Assert( 1 <= uLen && uLen <= MaxUTF8Len );
  101. if( uLen == 1 )
  102. {
  103. return bUTF8[0];
  104. }
  105. else if( uLen == 2 )
  106. {
  107. return ((bUTF8[0] & 0x1F) << 6 ) | (bUTF8[1] & 0x3F);
  108. }
  109. else if( uLen == 3 )
  110. {
  111. return ((bUTF8[0] & 0x0F) << 12) | ((bUTF8[1] & 0x3F) << 6) | (bUTF8[2] & 0x3F);
  112. }
  113. else
  114. {
  115. Assert( uLen == 4 );
  116. return ((bUTF8[0] & 0x07) << 18) | ((bUTF8[1] & 0x3F) << 12) | ((bUTF8[2] & 0x3F) << 6 ) | (bUTF8[3] & 0x3F) ;
  117. }
  118. }
  119. // The Encode algorithm described in sec. 15.1.3 of the spec. The input string is
  120. // 'strURI' and the Unescaped set is described by the flags 'unescapedFlags'. The
  121. // output is a string var.
  122. Var UriHelper::Encode(JavascriptString* strURI, unsigned char unescapedFlags, ScriptContext* scriptContext )
  123. {
  124. charcount_t len = strURI->GetLength();
  125. __in_ecount(len) const char16* input = strURI->GetString();
  126. bool needsChanges = false;
  127. BYTE bUTF8[MaxUTF8Len];
  128. // pass 1 calculate output length and error check
  129. uint32 outputLen = 0;
  130. for( uint32 k = 0; k < len; k++ )
  131. {
  132. char16 c = input[k];
  133. uint32 uVal;
  134. if( InURISet(c, unescapedFlags) )
  135. {
  136. outputLen = UInt32Math::Add(outputLen, 1);
  137. }
  138. else
  139. {
  140. needsChanges = true;
  141. if( c >= 0xDC00 && c <= 0xDFFF )
  142. {
  143. JavascriptError::ThrowURIError(scriptContext, JSERR_URIEncodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  144. }
  145. else if( c < 0xD800 || c > 0xDBFF )
  146. {
  147. uVal = (uint32)c;
  148. }
  149. else
  150. {
  151. ++k;
  152. if(k == len)
  153. {
  154. JavascriptError::ThrowURIError(scriptContext, JSERR_URIEncodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  155. }
  156. __analysis_assume(k < len); // because we throw exception if k==len
  157. char16 c1 = input[k];
  158. if( c1 < 0xDC00 || c1 > 0xDFFF )
  159. {
  160. JavascriptError::ThrowURIError(scriptContext, JSERR_URIEncodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  161. }
  162. uVal = (c - 0xD800) * 0x400 + (c1 - 0xDC00) + 0x10000;
  163. }
  164. uint32 utfLen = ToUTF8(uVal, bUTF8);
  165. utfLen = UInt32Math::Mul(utfLen, 3);
  166. outputLen = UInt32Math::Add(outputLen, utfLen);
  167. }
  168. }
  169. // If nothing needs encoding, then avoid extra work
  170. if (!needsChanges)
  171. {
  172. AssertMsg(scriptContext == strURI->GetScriptContext(), "Should have already marshaled the string in cross site thunk");
  173. return strURI;
  174. }
  175. //pass 2 generate the encoded URI
  176. uint32 allocSize = UInt32Math::Add(outputLen, 1);
  177. char16* outURI = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, allocSize);
  178. char16* outCurrent = outURI;
  179. const char16 *hexStream = _u("0123456789ABCDEF");
  180. for( uint32 k = 0; k < len; k++ )
  181. {
  182. char16 c = input[k];
  183. uint32 uVal;
  184. if( InURISet(c, unescapedFlags) )
  185. {
  186. __analysis_assume(outCurrent < outURI + allocSize);
  187. *outCurrent++ = c;
  188. }
  189. else
  190. {
  191. #if DBG
  192. if( c >= 0xDC00 && c <= 0xDFFF )
  193. {
  194. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  195. }
  196. #endif
  197. if( c < 0xD800 || c > 0xDBFF )
  198. {
  199. uVal = (uint32)c;
  200. }
  201. else
  202. {
  203. ++k;
  204. #if DBG
  205. if(k == len)
  206. {
  207. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  208. }
  209. #endif
  210. __analysis_assume(k < len);// because we throw exception if k==len
  211. char16 c1 = input[k];
  212. #if DBG
  213. if( c1 < 0xDC00 || c1 > 0xDFFF )
  214. {
  215. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  216. }
  217. #endif
  218. uVal = (c - 0xD800) * 0x400 + (c1 - 0xDC00) + 0x10000;
  219. }
  220. uint32 utfLen = ToUTF8(uVal, bUTF8);
  221. for( uint32 j = 0; j < utfLen; j++ )
  222. {
  223. #pragma prefast(disable: 26014, "buffer length was calculated earlier");
  224. BYTE val = bUTF8[j];
  225. *outCurrent++ = _u('%');
  226. *outCurrent++ = hexStream[(val >> 4)];
  227. *outCurrent++ = hexStream[(val & 0xF)];
  228. #pragma prefast(default: 26014);
  229. }
  230. }
  231. }
  232. AssertMsg(outURI + outputLen == outCurrent, " URI out buffer out of sync");
  233. __analysis_assume(outputLen + 1 == allocSize);
  234. outURI[outputLen] = _u('\0');
  235. return JavascriptString::NewWithBuffer(outURI, outputLen, scriptContext);
  236. }
  237. Var UriHelper::DecodeCoreURI(ScriptContext* scriptContext, Arguments& args, unsigned char reservedFlags )
  238. {
  239. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  240. JavascriptString * strURI;
  241. //TODO make sure this string is pinned when the memory recycler is in
  242. if(args.Info.Count < 2)
  243. {
  244. strURI = scriptContext->GetLibrary()->GetUndefinedDisplayString();
  245. }
  246. else
  247. {
  248. if (VarIs<JavascriptString>(args[1]))
  249. {
  250. strURI = VarTo<JavascriptString>(args[1]);
  251. }
  252. else
  253. {
  254. strURI = JavascriptConversion::ToString(args[1], scriptContext);
  255. }
  256. }
  257. return Decode(strURI, reservedFlags, scriptContext);
  258. }
  259. // The Decode algorithm described in sec. 15.1.3 of the spec. The input string is
  260. // 'strURI' and the Reserved set is described by the flags 'reservedFlags'. The
  261. // output is a string var.
  262. Var UriHelper::Decode(JavascriptString* strURI, unsigned char reservedFlags, ScriptContext* scriptContext)
  263. {
  264. charcount_t len = strURI->GetLength();
  265. __in_ecount(len) const char16* input = strURI->GetString();
  266. bool needsChanges = false;
  267. char16 c1;
  268. char16 c;
  269. // pass 1 calculate output length and error check
  270. uint32 outputLen = 0;
  271. for( uint32 k = 0; k < len; k++ )
  272. {
  273. c = input[k];
  274. if( c == '%')
  275. {
  276. needsChanges = true;
  277. uint32 start = k;
  278. if( k + 2 >= len )
  279. {
  280. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  281. }
  282. // %-encoded components in a URI may only contain hexadecimal digits from the ASCII character set. 'swscanf_s'
  283. // only supports those characters when decoding hexadecimal integers. 'iswxdigit' on the other hand, uses the
  284. // current locale to see if the specified character maps to a hexadecimal digit, which causes it to consider some
  285. // characters outside the ASCII character set to be hexadecimal digits, so we can't use that. 'swscanf_s' seems
  286. // to be overkill for this, so using a simple function that parses two hex digits and produces their value.
  287. BYTE b;
  288. if(!DecodeByteFromHex(input[k + 1], input[k + 2], b))
  289. {
  290. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError);
  291. }
  292. k += 2;
  293. if( (b & 0x80) == 0)
  294. {
  295. c1 = b;
  296. }
  297. else
  298. {
  299. int n;
  300. for( n = 1; ((b << n) & 0x80) != 0; n++ )
  301. ;
  302. if( n == 1 || n > UriHelper::MaxUTF8Len )
  303. {
  304. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  305. }
  306. BYTE bOctets[UriHelper::MaxUTF8Len];
  307. bOctets[0] = b;
  308. if( k + 3 * (n-1) >= len )
  309. {
  310. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  311. }
  312. for( int j = 1; j < n; j++ )
  313. {
  314. if( input[++k] != '%' )
  315. {
  316. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  317. }
  318. if(!DecodeByteFromHex(input[k + 1], input[k + 2], b))
  319. {
  320. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  321. }
  322. // The two leading bits should be 10 for a valid UTF-8 encoding
  323. if( (b & 0xC0) != 0x80)
  324. {
  325. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  326. }
  327. k += 2;
  328. bOctets[j] = b;
  329. }
  330. uint32 uVal = UriHelper::FromUTF8( bOctets, n );
  331. if( uVal >= 0xD800 && uVal <= 0xDFFF)
  332. {
  333. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  334. }
  335. if( uVal < 0x10000 )
  336. {
  337. c1 = (char16)uVal;
  338. }
  339. else if( uVal > 0x10ffff )
  340. {
  341. JavascriptError::ThrowURIError(scriptContext, JSERR_URIDecodeError /* TODO-ERROR: _u("NEED MESSAGE") */);
  342. }
  343. else
  344. {
  345. outputLen +=2;
  346. continue;
  347. }
  348. }
  349. if( ! UriHelper::InURISet( c1, reservedFlags ))
  350. {
  351. outputLen++;
  352. }
  353. else
  354. {
  355. outputLen += k - start + 1;
  356. }
  357. }
  358. else // c is not '%'
  359. {
  360. outputLen++;
  361. }
  362. }
  363. // If nothing needs decoding, then avoid extra work
  364. if (!needsChanges)
  365. {
  366. AssertMsg(scriptContext == strURI->GetScriptContext(), "Should have already marshaled the string in cross site thunk");
  367. return strURI;
  368. }
  369. //pass 2 generate the decoded URI
  370. uint32 allocSize = UInt32Math::Add(outputLen, 1);
  371. char16* outURI = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, allocSize);
  372. char16* outCurrent = outURI;
  373. for( uint32 k = 0; k < len; k++ )
  374. {
  375. c = input[k];
  376. if( c == '%')
  377. {
  378. uint32 start = k;
  379. #if DBG
  380. Assert(!(k + 2 >= len));
  381. if( k + 2 >= len )
  382. {
  383. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  384. }
  385. #endif
  386. // Let OACR know some things about 'k' that we checked just above, to let it know that we are not going to
  387. // overflow later. The same checks are done in the first pass in non-debug builds, and the conditions
  388. // checked upon in the first and second pass are the same.
  389. __analysis_assume(!(k + 2 >= len));
  390. BYTE b;
  391. if(!DecodeByteFromHex(input[k + 1], input[k + 2], b))
  392. {
  393. #if DBG
  394. AssertMsg(false, "!DecodeByteFromHex(input[k + 1], input[k + 2], b)");
  395. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  396. #endif
  397. }
  398. k += 2;
  399. if( (b & 0x80) == 0)
  400. {
  401. c1 = b;
  402. }
  403. else
  404. {
  405. int n;
  406. for( n = 1; ((b << n) & 0x80) != 0; n++ )
  407. ;
  408. if( n == 1 || n > UriHelper::MaxUTF8Len )
  409. {
  410. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  411. }
  412. BYTE bOctets[UriHelper::MaxUTF8Len];
  413. bOctets[0] = b;
  414. #if DBG
  415. Assert(!(k + 3 * (n-1) >= len));
  416. if( k + 3 * (n-1) >= len )
  417. {
  418. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  419. }
  420. #endif
  421. // Let OACR know some things about 'k' that we checked just above, to let it know that we are not going to
  422. // overflow later. The same checks are done in the first pass in non-debug builds, and the conditions
  423. // checked upon in the first and second pass are the same.
  424. __analysis_assume(!(k + 3 * (n-1) >= len));
  425. for( int j = 1; j < n; j++ )
  426. {
  427. ++k;
  428. #if DBG
  429. Assert(!(input[k] != '%'));
  430. if( input[k] != '%' )
  431. {
  432. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  433. }
  434. #endif
  435. if(!DecodeByteFromHex(input[k + 1], input[k + 2], b))
  436. {
  437. #if DBG
  438. AssertMsg(false, "!DecodeByteFromHex(input[k + 1], input[k + 2], b)");
  439. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  440. #endif
  441. }
  442. #if DBG
  443. // The two leading bits should be 10 for a valid UTF-8 encoding
  444. Assert(!((b & 0xC0) != 0x80));
  445. if( (b & 0xC0) != 0x80)
  446. {
  447. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  448. }
  449. #endif
  450. k += 2;
  451. bOctets[j] = b;
  452. }
  453. uint32 uVal = UriHelper::FromUTF8( bOctets, n );
  454. #if DBG
  455. Assert(!(uVal >= 0xD800 && uVal <= 0xDFFF));
  456. if( uVal >= 0xD800 && uVal <= 0xDFFF)
  457. {
  458. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  459. }
  460. #endif
  461. if( uVal < 0x10000 )
  462. {
  463. c1 = (char16)uVal;
  464. }
  465. #if DBG
  466. else if( uVal > 0x10ffff )
  467. {
  468. AssertMsg(false, "uVal > 0x10ffff");
  469. JavascriptError::ThrowURIError(scriptContext, VBSERR_InternalError /* TODO-ERROR: _u("NEED MESSAGE") */);
  470. }
  471. #endif
  472. else
  473. {
  474. uint32 l = (( uVal - 0x10000) & 0x3ff) + 0xdc00;
  475. uint32 h = ((( uVal - 0x10000) >> 10) & 0x3ff) + 0xd800;
  476. __analysis_assume(outCurrent + 2 <= outURI + allocSize);
  477. *outCurrent++ = (char16)h;
  478. *outCurrent++ = (char16)l;
  479. continue;
  480. }
  481. }
  482. if( !UriHelper::InURISet( c1, reservedFlags ))
  483. {
  484. __analysis_assume(outCurrent < outURI + allocSize);
  485. *outCurrent++ = c1;
  486. }
  487. else
  488. {
  489. js_memcpy_s(outCurrent, (allocSize - (outCurrent - outURI)) * sizeof(char16), &input[start], (k - start + 1)*sizeof(char16));
  490. outCurrent += k - start + 1;
  491. }
  492. }
  493. else // c is not '%'
  494. {
  495. __analysis_assume(outCurrent < outURI + allocSize);
  496. *outCurrent++ = c;
  497. }
  498. }
  499. AssertMsg(outURI + outputLen == outCurrent, " URI out buffer out of sync");
  500. __analysis_assume(outputLen + 1 == allocSize);
  501. outURI[outputLen] = _u('\0');
  502. return JavascriptString::NewWithBuffer(outURI, outputLen, scriptContext);
  503. }
  504. // Decodes a two-hexadecimal-digit wide character pair into the byte value it represents
  505. bool UriHelper::DecodeByteFromHex(const char16 digit1, const char16 digit2, unsigned char &value)
  506. {
  507. int x;
  508. if(!Js::NumberUtilities::FHexDigit(digit1, &x))
  509. {
  510. return false;
  511. }
  512. Assert(static_cast<unsigned int>(x) <= 0xfU);
  513. value = static_cast<unsigned char>(x) << 4;
  514. if(!Js::NumberUtilities::FHexDigit(digit2, &x))
  515. {
  516. return false;
  517. }
  518. Assert(static_cast<unsigned int>(x) <= 0xfU);
  519. value += static_cast<unsigned char>(x);
  520. return true;
  521. }
  522. }