PageRenderTime 88ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 1ms

/3.0/Source/ClassLibrary/Strings.cs

#
C# | 6704 lines | 4222 code | 895 blank | 1587 comment | 1044 complexity | 972973919d58ab7a7e102243ed5350a1 MD5 | raw file
Possible License(s): CPL-1.0, GPL-2.0, CC-BY-SA-3.0, MPL-2.0-no-copyleft-exception, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. Copyright (c) 2004-2006 Tomas Matousek and Ladislav Prosek.
  3. The use and distribution terms for this software are contained in the file named License.txt,
  4. which can be found in the root of the Phalanger distribution. By using this software
  5. in any fashion, you are agreeing to be bound by the terms of this license.
  6. You must not remove this notice from this software.
  7. */
  8. /*
  9. GENERICS:
  10. Generic char map and hashtable will allow to handle all Unicode characters and get rid of the errors:
  11. <exception cref="PhpException"><paramref name="whiteSpaceCharacters"/> contains Unicode characters greater than '\u0800'.</exception>
  12. TODO:
  13. - PHP6 - new functions hash($alg,...) hash_file($alg, ...)
  14. - Added overflow checks to wordwrap() function. (5.1.3)
  15. - Fixed offset/length parameter validation in substr_compare() function. (5.1.3)
  16. - (strncmp & strncasecmp do not return false on negative string length). (5.1.3)
  17. */
  18. using System;
  19. using PHP.Core;
  20. using System.IO;
  21. using System.Text;
  22. using System.Collections;
  23. using System.Collections.Generic;
  24. using System.Security.Cryptography;
  25. using System.Text.RegularExpressions;
  26. using System.ComponentModel;
  27. #if SILVERLIGHT
  28. using PHP.CoreCLR;
  29. using System.Windows.Browser;
  30. #else
  31. using System.Web;
  32. #endif
  33. namespace PHP.Library
  34. {
  35. #region Enumerations
  36. /// <summary>Quote conversion options.</summary>
  37. [Flags]
  38. public enum QuoteStyle
  39. {
  40. /// <summary>
  41. /// Default quote style for <c>htmlentities</c>.
  42. /// </summary>
  43. HtmlEntitiesDefault = QuoteStyle.Compatible | QuoteStyle.Html401,
  44. /// <summary>Single quotes.</summary>
  45. SingleQuotes = 1,
  46. /// <summary>Double quotes.</summary>
  47. DoubleQuotes = 2,
  48. /// <summary>
  49. /// No quotes.
  50. /// Will leave both double and single quotes unconverted.
  51. /// </summary>
  52. [ImplementsConstant("ENT_NOQUOTES")]
  53. NoQuotes = 0,
  54. /// <summary>
  55. /// Will convert double-quotes and leave single-quotes alone.
  56. /// </summary>
  57. [ImplementsConstant("ENT_COMPAT")]
  58. Compatible = DoubleQuotes,
  59. /// <summary>
  60. /// Both single and double quotes.
  61. /// Will convert both double and single quotes.
  62. /// </summary>
  63. [ImplementsConstant("ENT_QUOTES")]
  64. BothQuotes = DoubleQuotes | SingleQuotes,
  65. /// <summary>
  66. /// Silently discard invalid code unit sequences instead of
  67. /// returning an empty string. Using this flag is discouraged
  68. /// as it may have security implications.
  69. /// </summary>
  70. [ImplementsConstant("ENT_IGNORE")]
  71. Ignore = 4,
  72. /// <summary>
  73. /// Replace invalid code unit sequences with a Unicode
  74. /// Replacement Character U+FFFD (UTF-8) or &amp;#FFFD;
  75. /// (otherwise) instead of returning an empty string.
  76. /// </summary>
  77. [ImplementsConstant("ENT_SUBSTITUTE")] // 8
  78. Substitute = 8,
  79. /// <summary>
  80. /// Handle code as HTML 4.01.
  81. /// </summary>
  82. [ImplementsConstant("ENT_HTML401")] // 0
  83. Html401 = NoQuotes,
  84. /// <summary>
  85. /// Handle code as XML 1.
  86. /// </summary>
  87. [ImplementsConstant("ENT_XML1")] // 16
  88. XML1 = 16,
  89. /// <summary>
  90. /// Handle code as XHTML.
  91. /// </summary>
  92. [ImplementsConstant("ENT_XHTML")] // 32
  93. XHTML = 32,
  94. /// <summary>
  95. /// Handle code as HTML 5.
  96. /// </summary>
  97. [ImplementsConstant("ENT_HTML5")] // (16|32)
  98. HTML5 = XML1 | XHTML,
  99. /// <summary>
  100. /// Replace invalid code points for the given document type
  101. /// with a Unicode Replacement Character U+FFFD (UTF-8) or &amp;#FFFD;
  102. /// (otherwise) instead of leaving them as is.
  103. /// This may be useful, for instance, to ensure the well-formedness
  104. /// of XML documents with embedded external content.
  105. /// </summary>
  106. [ImplementsConstant("ENT_DISALLOWED")] // 128
  107. Disallowed = 128,
  108. };
  109. /// <summary>Types of HTML entities tables.</summary>
  110. public enum HtmlEntitiesTable
  111. {
  112. /// <summary>Table containing special characters only.</summary>
  113. [ImplementsConstant("HTML_SPECIALCHARS")]
  114. SpecialChars = 0,
  115. /// <summary>Table containing all entities.</summary>
  116. [ImplementsConstant("HTML_ENTITIES")]
  117. AllEntities = 1
  118. };
  119. /// <summary>
  120. /// Type of padding.
  121. /// </summary>
  122. public enum PaddingType
  123. {
  124. /// <summary>Pad a string from the left.</summary>
  125. [ImplementsConstant("STR_PAD_LEFT")]
  126. Left = 0,
  127. /// <summary>Pad a string from the right.</summary>
  128. [ImplementsConstant("STR_PAD_RIGHT")]
  129. Right = 1,
  130. /// <summary>Pad a string from both sides.</summary>
  131. [ImplementsConstant("STR_PAD_BOTH")]
  132. Both = 2
  133. }
  134. /// <summary>
  135. /// Format of a return value of <see cref="PhpStrings.CountWords"/> method. Constants are not named in PHP.
  136. /// </summary>
  137. public enum WordCountResult
  138. {
  139. /// <summary>
  140. /// Return number of words in string.
  141. /// </summary>
  142. WordCount = 0,
  143. /// <summary>
  144. /// Return array of words.
  145. /// </summary>
  146. WordsArray = 1,
  147. /// <summary>
  148. /// Return positions to words mapping.
  149. /// </summary>
  150. PositionsToWordsMapping = 2
  151. }
  152. #endregion
  153. /// <summary>
  154. /// Manipulates strings.
  155. /// </summary>
  156. /// <threadsafety static="true"/>
  157. public static class PhpStrings
  158. {
  159. #region Character map
  160. #if !SILVERLIGHT
  161. [ThreadStatic]
  162. #endif
  163. private static CharMap _charmap;
  164. /// <summary>
  165. /// Get clear <see cref="CharMap"/> to be used by current thread. <see cref="_charmap"/>.
  166. /// </summary>
  167. internal static CharMap InitializeCharMap()
  168. {
  169. CharMap result = _charmap;
  170. if (result == null)
  171. _charmap = result = new CharMap(0x0800);
  172. else
  173. result.ClearAll();
  174. return result;
  175. }
  176. #endregion
  177. #region Binary Data Functions
  178. #region ord, chr, bin2hex, ord_unicode, chr_unicode, bin2hex_unicode, to_binary
  179. /// <summary>
  180. /// Returns ASCII code of the first character of a string of bytes.
  181. /// </summary>
  182. /// <param name="bytes">The string of bytes which the first byte will be returned.</param>
  183. /// <returns>The ASCII code of <paramref name="bytes"/>[0] or zero if null or empty.</returns>
  184. [ImplementsFunction("ord")]
  185. [PureFunction]
  186. public static int Ord(PhpBytes bytes)
  187. {
  188. return (bytes == null || bytes.Length == 0) ? 0 : (int)bytes[0];
  189. }
  190. /// <summary>
  191. /// Returns Unicode ordinal number of the first character of a string.
  192. /// </summary>
  193. /// <param name="str">The string which the first character's ordinal number is returned.</param>
  194. /// <returns>The ordinal number of <paramref name="str"/>[0].</returns>
  195. [ImplementsFunction("ord_unicode")]
  196. [PureFunction]
  197. public static int OrdUnicode(string str)
  198. {
  199. return (str == null || str == String.Empty) ? 0 : (int)str[0];
  200. }
  201. /// <summary>
  202. /// Converts ordinal number of character to a binary string containing that character.
  203. /// </summary>
  204. /// <param name="charCode">The ASCII code.</param>
  205. /// <returns>The character with <paramref name="charCode"/> ASCIT code.</returns>
  206. /// <remarks>Current code-page is determined by the <see cref="ApplicationConfiguration.GlobalizationSection.PageEncoding"/> property.</remarks>
  207. [ImplementsFunction("chr")]
  208. [PureFunction]
  209. public static PhpBytes Chr(int charCode)
  210. {
  211. return new PhpBytes(unchecked((byte)charCode));
  212. }
  213. /// <summary>
  214. /// Converts ordinal number of Unicode character to a string containing that character.
  215. /// </summary>
  216. /// <param name="charCode">The ordinal number of character.</param>
  217. /// <returns>The character with <paramref name="charCode"/> ordnial number.</returns>
  218. [ImplementsFunction("chr_unicode")]
  219. [PureFunction]
  220. public static string ChrUnicode(int charCode)
  221. {
  222. return unchecked((char)charCode).ToString();
  223. }
  224. /// <summary>
  225. /// Converts a string of bytes into hexadecimal representation.
  226. /// </summary>
  227. /// <param name="bytes">The string of bytes.</param>
  228. /// <returns>Concatenation of hexadecimal values of bytes of <paramref name="bytes"/>.</returns>
  229. /// <example>
  230. /// The string "01A" is converted into string "303140" because ord('0') = 0x30, ord('1') = 0x31, ord('A') = 0x40.
  231. /// </example>
  232. [ImplementsFunction("bin2hex")]
  233. [PureFunction]
  234. public static string BinToHex(PhpBytes bytes)
  235. {
  236. return (bytes == null) ? String.Empty : StringUtils.BinToHex(bytes.ReadonlyData, null);
  237. }
  238. /// <summary>
  239. /// Converts a string into hexadecimal representation.
  240. /// </summary>
  241. /// <param name="str">The string to be converted.</param>
  242. /// <returns>
  243. /// The concatenated four-characters long hexadecimal numbers each representing one character of <paramref name="str"/>.
  244. /// </returns>
  245. [ImplementsFunction("bin2hex_unicode")]
  246. [PureFunction]
  247. public static string BinToHex(string str)
  248. {
  249. if (str == null) return null;
  250. int length = str.Length;
  251. StringBuilder result = new StringBuilder(length * 4, length * 4);
  252. result.Length = length * 4;
  253. const string hex_digs = "0123456789abcdef";
  254. for (int i = 0; i < length; i++)
  255. {
  256. int c = (int)str[i];
  257. result[4 * i + 0] = hex_digs[(c & 0xf000) >> 12];
  258. result[4 * i + 1] = hex_digs[(c & 0x0f00) >> 8];
  259. result[4 * i + 2] = hex_digs[(c & 0x00f0) >> 4];
  260. result[4 * i + 3] = hex_digs[(c & 0x000f)];
  261. }
  262. return result.ToString();
  263. }
  264. /// <summary>
  265. /// Converts a variable to a string of binary data.
  266. /// </summary>
  267. /// <param name="var">A variable.</param>
  268. /// <returns>Binary data.</returns>
  269. [ImplementsFunction("to_binary")]
  270. [PureFunction]
  271. public static PhpBytes ToBinary(PhpBytes var)
  272. {
  273. return var;
  274. }
  275. #endregion
  276. #region convert_cyr_string
  277. #region cyrWin1251 (1251), cyrCp866 (20866), cyrIso88595 (28595), cyrMac (10007) conversion tables
  278. /// <summary>
  279. /// Cyrillic translation table for Windows CP1251 character set.
  280. /// </summary>
  281. private static readonly byte[] cyrWin1251 = new byte[]
  282. {
  283. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  284. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  285. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  286. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  287. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  288. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  289. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  290. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  291. 46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,
  292. 46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,
  293. 154,174,190,46,159,189,46,46,179,191,180,157,46,46,156,183,
  294. 46,46,182,166,173,46,46,158,163,152,164,155,46,46,46,167,
  295. 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
  296. 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
  297. 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
  298. 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209,
  299. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  300. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  301. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  302. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  303. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  304. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  305. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  306. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  307. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  308. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  309. 32,32,32,184,186,32,179,191,32,32,32,32,32,180,162,32,
  310. 32,32,32,168,170,32,178,175,32,32,32,32,32,165,161,169,
  311. 254,224,225,246,228,229,244,227,245,232,233,234,235,236,237,238,
  312. 239,255,240,241,242,243,230,226,252,251,231,248,253,249,247,250,
  313. 222,192,193,214,196,197,212,195,213,200,201,202,203,204,205,206,
  314. 207,223,208,209,210,211,198,194,220,219,199,216,221,217,215,218,
  315. };
  316. /// <summary>
  317. /// Cyrillic translation table for CP866 character set.
  318. /// </summary>
  319. private static readonly byte[] cyrCp866 = new byte[]
  320. {
  321. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  322. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  323. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  324. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  325. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  326. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  327. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  328. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  329. 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
  330. 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
  331. 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
  332. 35,35,35,124,124,124,124,43,43,124,124,43,43,43,43,43,
  333. 43,45,45,124,45,43,124,124,43,43,45,45,124,45,43,45,
  334. 45,45,45,43,43,43,43,43,43,43,43,35,35,124,124,35,
  335. 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209,
  336. 179,163,180,164,183,167,190,174,32,149,158,32,152,159,148,154,
  337. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  338. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  339. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  340. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  341. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  342. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  343. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  344. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  345. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  346. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  347. 205,186,213,241,243,201,32,245,187,212,211,200,190,32,247,198,
  348. 199,204,181,240,242,185,32,244,203,207,208,202,216,32,246,32,
  349. 238,160,161,230,164,165,228,163,229,168,169,170,171,172,173,174,
  350. 175,239,224,225,226,227,166,162,236,235,167,232,237,233,231,234,
  351. 158,128,129,150,132,133,148,131,149,136,137,138,139,140,141,142,
  352. 143,159,144,145,146,147,134,130,156,155,135,152,157,153,151,154,
  353. };
  354. /// <summary>
  355. /// Cyrillic translation table for ISO88595 character set.
  356. /// </summary>
  357. private static readonly byte[] cyrIso88595 = new byte[]
  358. {
  359. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  360. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  361. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  362. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  363. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  364. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  365. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  366. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  367. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  368. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  369. 32,179,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  370. 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
  371. 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
  372. 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
  373. 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209,
  374. 32,163,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  375. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  376. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  377. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  378. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  379. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  380. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  381. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  382. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  383. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  384. 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
  385. 32,32,32,241,32,32,32,32,32,32,32,32,32,32,32,32,
  386. 32,32,32,161,32,32,32,32,32,32,32,32,32,32,32,32,
  387. 238,208,209,230,212,213,228,211,229,216,217,218,219,220,221,222,
  388. 223,239,224,225,226,227,214,210,236,235,215,232,237,233,231,234,
  389. 206,176,177,198,180,181,196,179,197,184,185,186,187,188,189,190,
  390. 191,207,192,193,194,195,182,178,204,203,183,200,205,201,199,202,
  391. };
  392. /// <summary>
  393. /// Cyrillic translation table for Mac character set.
  394. /// </summary>
  395. private static readonly byte[] cyrMac = new byte[]
  396. {
  397. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  398. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  399. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  400. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  401. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  402. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  403. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  404. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  405. 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
  406. 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
  407. 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
  408. 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
  409. 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
  410. 144,145,146,147,148,149,150,151,152,153,154,155,156,179,163,209,
  411. 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
  412. 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,255,
  413. 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  414. 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
  415. 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
  416. 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
  417. 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
  418. 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
  419. 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
  420. 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
  421. 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
  422. 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
  423. 160,161,162,222,164,165,166,167,168,169,170,171,172,173,174,175,
  424. 176,177,178,221,180,181,182,183,184,185,186,187,188,189,190,191,
  425. 254,224,225,246,228,229,244,227,245,232,233,234,235,236,237,238,
  426. 239,223,240,241,242,243,230,226,252,251,231,248,253,249,247,250,
  427. 158,128,129,150,132,133,148,131,149,136,137,138,139,140,141,142,
  428. 143,159,144,145,146,147,134,130,156,155,135,152,157,153,151,154,
  429. };
  430. #endregion
  431. /// <summary>
  432. /// Returns a Cyrillic translation table for a specified character set,
  433. /// </summary>
  434. /// <param name="code">The character set code. Can be one of 'k', 'w', 'i', 'a', 'd', 'm'.</param>
  435. /// <returns>The translation table or null if no table is associated with given charset code.</returns>
  436. internal static byte[] GetCyrTableInternal(char code)
  437. {
  438. switch (Char.ToUpper(code))
  439. {
  440. case 'W':
  441. return cyrWin1251;
  442. case 'A':
  443. case 'D':
  444. return cyrCp866;
  445. case 'I':
  446. return cyrIso88595;
  447. case 'M':
  448. return cyrMac;
  449. case 'K':
  450. return null;
  451. default:
  452. return ArrayUtils.EmptyBytes;
  453. }
  454. }
  455. /// <include file='Doc/Strings.xml' path='docs/method[@name="ConvertCyrillic"]/*'/>
  456. /// <exception cref="PhpException">Thrown if source or destination charset is invalid. </exception>
  457. [ImplementsFunction("convert_cyr_string")]
  458. public static PhpBytes ConvertCyrillic(PhpBytes bytes, string srcCharset, string dstCharset)
  459. {
  460. if (bytes == null) return null;
  461. if (bytes.Length == 0) return PhpBytes.Empty;
  462. // checks srcCharset argument:
  463. if (srcCharset == null || srcCharset == String.Empty)
  464. {
  465. PhpException.InvalidArgument("srcCharset", LibResources.GetString("arg:null_or_empty"));
  466. return PhpBytes.Empty;
  467. }
  468. // checks dstCharset argument:
  469. if (dstCharset == null || dstCharset == String.Empty)
  470. {
  471. PhpException.InvalidArgument("dstCharset", LibResources.GetString("arg:null_or_empty"));
  472. return PhpBytes.Empty;
  473. }
  474. // get and check source charset table:
  475. byte[] fromTable = GetCyrTableInternal(srcCharset[0]);
  476. if (fromTable != null && fromTable.Length < 256)
  477. {
  478. PhpException.Throw(PhpError.Warning, LibResources.GetString("invalid_src_charser"));
  479. return PhpBytes.Empty;
  480. }
  481. // get and check destination charset table:
  482. byte[] toTable = GetCyrTableInternal(dstCharset[0]);
  483. if (toTable != null && toTable.Length < 256)
  484. {
  485. PhpException.Throw(PhpError.Warning, LibResources.GetString("invalid_dst_charser"));
  486. return PhpBytes.Empty;
  487. }
  488. byte[] data = bytes.ReadonlyData;
  489. byte[] result = new byte[data.Length];
  490. // perform conversion:
  491. if (fromTable == null)
  492. {
  493. if (toTable != null)
  494. {
  495. for (int i = 0; i < data.Length; i++) result[i] = toTable[data[i] + 256];
  496. }
  497. }
  498. else
  499. {
  500. if (toTable == null)
  501. {
  502. for (int i = 0; i < data.Length; i++) result[i] = fromTable[data[i]];
  503. }
  504. else
  505. {
  506. for (int i = 0; i < data.Length; i++) result[i] = toTable[fromTable[data[i]] + 256];
  507. }
  508. }
  509. return new PhpBytes(result);
  510. }
  511. #endregion
  512. #region count_chars
  513. /// <summary>
  514. /// Creates a histogram of Unicode character occurence in the given string.
  515. /// </summary>
  516. /// <param name="str">The string to be processed.</param>
  517. /// <returns>The array of characters frequency (unsorted).</returns>
  518. [ImplementsFunction("count_chars_unicode")]
  519. public static PhpArray CountChars(string str)
  520. {
  521. PhpArray count = new PhpArray();
  522. for (int i = str.Length - 1; i >= 0; i--)
  523. {
  524. int j = (int)str[i];
  525. object c = count[j];
  526. count[j] = (c == null) ? 1 : (int)c + 1;
  527. }
  528. return count;
  529. }
  530. /// <summary>
  531. /// Creates a histogram of byte occurence in the given array of bytes.
  532. /// </summary>
  533. /// <param name="bytes">The array of bytes to be processed.</param>
  534. /// <returns>The array of bytes frequency.</returns>
  535. public static int[] CountBytes(byte[] bytes)
  536. {
  537. if (bytes == null)
  538. throw new ArgumentNullException("bytes");
  539. int[] count = new int[256];
  540. for (int i = bytes.Length - 1; i >= 0; i--)
  541. count[bytes[i]]++;
  542. return count;
  543. }
  544. /// <summary>
  545. /// Creates a histogram of byte occurrence in specified string of bytes.
  546. /// </summary>
  547. /// <param name="bytes">Bytes to be processed.</param>
  548. /// <returns>The array of characters frequency.</returns>
  549. [ImplementsFunction("count_chars")]
  550. public static PhpArray CountChars(PhpBytes bytes)
  551. {
  552. return (bytes == null) ? new PhpArray() : new PhpArray(CountBytes(bytes.ReadonlyData), 0, 256);
  553. }
  554. /// <summary>
  555. /// Creates a histogram of character occurence in a string or string of bytes.
  556. /// </summary>
  557. /// <param name="data">The string or bytes to be processed.</param>
  558. /// <param name="mode">Determines the type of result.</param>
  559. /// <returns>Depending on <paramref name="mode"/> the following is returned:
  560. /// <list type="bullet">
  561. /// <item><term>0</term><description>an array with the character ordinals as key and their frequency as value,</description></item>
  562. /// <item><term>1</term><description>same as 0 but only characters with a frequency greater than zero are listed,</description></item>
  563. /// <item><term>2</term><description>same as 0 but only characters with a frequency equal to zero are listed,</description></item>
  564. /// <item><term>3</term><description>a string containing all used characters is returned,</description></item>
  565. /// <item><term>4</term><description>a string containing all not used characters is returned.</description></item>
  566. /// </list>
  567. /// </returns>
  568. /// <exception cref="PhpException">The <paramref name="mode"/> is invalid.</exception>
  569. /// <exception cref="PhpException">The <paramref name="data"/> contains Unicode characters greater than '\u0800'.</exception>
  570. [ImplementsFunction("count_chars")]
  571. public static object CountChars(object data, int mode)
  572. {
  573. try
  574. {
  575. switch (mode)
  576. {
  577. case 0: return new PhpArray(CountBytes(Core.Convert.ObjectToPhpBytes(data).ReadonlyData), 0, 256);
  578. case 1: return new PhpArray(CountBytes(Core.Convert.ObjectToPhpBytes(data).ReadonlyData), 0, 256, 0, true);
  579. case 2: return new PhpArray(CountBytes(Core.Convert.ObjectToPhpBytes(data).ReadonlyData), 0, 256, 0, false);
  580. case 3: return GetBytesContained(Core.Convert.ObjectToPhpBytes(data), 0, 255);
  581. case 4: return GetBytesNotContained(Core.Convert.ObjectToPhpBytes(data), 0, 255);
  582. default: PhpException.InvalidArgument("mode"); return null;
  583. }
  584. }
  585. catch (IndexOutOfRangeException)
  586. {
  587. // thrown by char map:
  588. PhpException.Throw(PhpError.Warning, LibResources.GetString("too_big_unicode_character"));
  589. return null;
  590. }
  591. }
  592. /// <summary>
  593. /// Returns a <see cref="String"/> containing all characters used in the specified <see cref="String"/>.
  594. /// </summary>
  595. /// <param name="str">The string to process.</param>
  596. /// <param name="lower">The lower limit for returned chars.</param>
  597. /// <param name="upper">The upper limit for returned chars.</param>
  598. /// <returns>
  599. /// The string containing characters used in <paramref name="str"/> which are sorted according to their ordinal values.
  600. /// </returns>
  601. /// <exception cref="IndexOutOfRangeException"><paramref name="str"/> contains characters greater than '\u0800'.</exception>
  602. public static string GetCharactersContained(string str, char lower, char upper)
  603. {
  604. CharMap charmap = InitializeCharMap();
  605. charmap.Add(str);
  606. return charmap.ToString(lower, upper, false);
  607. }
  608. /// <summary>
  609. /// Returns a <see cref="String"/> containing all characters used in the specified <see cref="String"/>.
  610. /// </summary>
  611. /// <param name="str">The string to process.</param>
  612. /// <param name="lower">The lower limit for returned chars.</param>
  613. /// <param name="upper">The upper limit for returned chars.</param>
  614. /// <returns>
  615. /// The string containing characters used in <paramref name="str"/> which are sorted according to their ordinal values.
  616. /// </returns>
  617. /// <exception cref="IndexOutOfRangeException"><paramref name="str"/> contains characters greater than '\u0800'.</exception>
  618. public static string GetCharactersNotContained(string str, char lower, char upper)
  619. {
  620. CharMap charmap = InitializeCharMap();
  621. charmap.Add(str);
  622. return charmap.ToString(lower, upper, true);
  623. }
  624. private static BitArray CreateByteMap(PhpBytes/*!*/ bytes, out int count)
  625. {
  626. BitArray map = new BitArray(256);
  627. map.Length = 256;
  628. count = 0;
  629. for (int i = 0; i < bytes.Length; i++)
  630. {
  631. if (!map[bytes[i]])
  632. {
  633. map[bytes[i]] = true;
  634. count++;
  635. }
  636. }
  637. return map;
  638. }
  639. public static PhpBytes GetBytesContained(PhpBytes bytes, byte lower, byte upper)
  640. {
  641. if (bytes == null) bytes = PhpBytes.Empty;
  642. int count;
  643. BitArray map = CreateByteMap(bytes, out count);
  644. byte[] result = new byte[count];
  645. int j = 0;
  646. for (int i = lower; i <= upper; i++)
  647. {
  648. if (map[i]) result[j++] = (byte)i;
  649. }
  650. return new PhpBytes(result);
  651. }
  652. public static PhpBytes GetBytesNotContained(PhpBytes bytes, byte lower, byte upper)
  653. {
  654. if (bytes == null) bytes = PhpBytes.Empty;
  655. int count;
  656. BitArray map = CreateByteMap(bytes, out count);
  657. byte[] result = new byte[map.Length - count];
  658. int j = 0;
  659. for (int i = lower; i <= upper; i++)
  660. {
  661. if (!map[i]) result[j++] = (byte)i;
  662. }
  663. return new PhpBytes(result);
  664. }
  665. #endregion
  666. #region crypt (CLR only)
  667. #if !SILVERLIGHT
  668. /// <summary>
  669. /// Specifies whether standard DES algorithm is implemented.
  670. /// We set it to 1, but it's not really true - our DES encryption is nothing like PHP's, so the values will be different
  671. /// If you want key compatibility with PHP, use CRYPT_MD5 by passing in a key starting with "?1?"
  672. /// </summary>
  673. [ImplementsConstant("CRYPT_STD_DES")]
  674. public const int CryptStandardDES = 1;
  675. /// <summary>
  676. /// Specifies whether extended DES algorithm is implemented.
  677. /// </summary>
  678. [ImplementsConstant("CRYPT_EXT_DES")]
  679. public const int CryptExtendedDES = 0;
  680. /// <summary>
  681. /// Specifies whether MD5 algorithm is implemented.
  682. /// </summary>
  683. [ImplementsConstant("CRYPT_MD5")]
  684. public const int CryptMD5 = 1;
  685. /// <summary>
  686. /// Specifies whether Blowfish encryption is implemented.
  687. /// </summary>
  688. [ImplementsConstant("CRYPT_BLOWFISH")]
  689. public const int CryptBlowfish = 0;
  690. /// <summary>
  691. /// Specifies the length of the salt applicable to the <see cref="Encrypt"/> method.
  692. /// </summary>
  693. [ImplementsConstant("CRYPT_SALT_LENGTH")]
  694. public const int CryptSaltLength = 9;
  695. /// <summary>
  696. /// Encrypts a string (one-way) with a random key.
  697. /// </summary>
  698. /// <param name="str">The string to encrypt.</param>
  699. /// <returns>The encrypted string.</returns>
  700. [ImplementsFunction("crypt")]
  701. public static PhpBytes Encrypt(PhpBytes str)
  702. {
  703. return Encrypt(str, null);
  704. }
  705. private const int MaxMD5Key = 12;
  706. private const int InternalMD5Key = 8;
  707. private const int MaxKeyLength = MaxMD5Key;
  708. private const int MaxDESKey = 8;
  709. public static bool ByteArrayEquals(byte[] array1, byte[] array2, int compareLength)
  710. {
  711. // If the other object is null, of a diffent type, or
  712. // of an array of a different length then skip out now.
  713. if ((array2 == null) || (array1 == null) || (compareLength <= 0 && (array1.Length != array2.Length)))
  714. return false;
  715. int minArray = Math.Min(array1.Length, array2.Length);
  716. if (compareLength <= 0)
  717. compareLength = minArray;
  718. else
  719. compareLength = Math.Min(minArray, compareLength);
  720. // If any of the elements are not equal, skip out.
  721. for (int i = 0; i < compareLength; ++i)
  722. if (array1[i] != array2[i])
  723. return false;
  724. // They're both the same length and the elements are all
  725. // equal so consider the arrays to be equal.
  726. return true;
  727. }
  728. //PHP's non-standard base64 used in converting md5 binary crypt() into chars
  729. //0 ... 63 => ascii - 64
  730. //aka bin_to_ascii ((c) >= 38 ? ((c) - 38 + 'a') : (c) >= 12 ? ((c) - 12 + 'A') : (c) + '.')
  731. private static byte[] itoa64 = System.Text.Encoding.ASCII.GetBytes("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
  732. private static void to64(MemoryStream stream, UInt32 v, int n)
  733. {
  734. while (--n >= 0)
  735. {
  736. stream.WriteByte(itoa64[v & 0x3f]);
  737. v >>= 6;
  738. }
  739. }
  740. private static byte[] MD5MagicString = System.Text.Encoding.ASCII.GetBytes("$1$");
  741. private static PhpBytes DoMD5Password(byte[] key, PhpBytes password)
  742. {
  743. MD5CryptoServiceProvider ctx = new MD5CryptoServiceProvider(), ctx1 = new MD5CryptoServiceProvider();
  744. MemoryStream result = new MemoryStream();
  745. byte[] final = new byte[16];
  746. int startOffset = 0, endOffset = 0;
  747. /* If it starts with the magic string, then skip that */
  748. if (ByteArrayEquals(key, MD5MagicString, MD5MagicString.Length))
  749. startOffset += MD5MagicString.Length;
  750. /* It stops at the first '$', max InternalMD5Key chars */
  751. for (endOffset = startOffset; key[endOffset] != '\0' && key[endOffset] != '$' && endOffset < (startOffset + InternalMD5Key); ++endOffset)
  752. continue;
  753. int keyLength = endOffset - startOffset;
  754. // PHP puts the relevant salt characters in the beginning
  755. result.Write(MD5MagicString, 0, MD5MagicString.Length);
  756. result.Write(key, startOffset, keyLength);
  757. result.Write(System.Text.Encoding.ASCII.GetBytes(new char[] { '$' }), 0, 1);
  758. ctx.Initialize();
  759. /* The password first, since that is what is most unknown */
  760. ctx.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
  761. ctx.TransformBlock(MD5MagicString, 0, MD5MagicString.Length, null, 0);
  762. ctx.TransformBlock(key, startOffset, keyLength, null, 0);
  763. ctx1.Initialize();
  764. /* Then just as many characters of the MD5(pw,salt,pw) */
  765. ctx1.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
  766. ctx1.TransformBlock(key, startOffset, keyLength, null, 0);
  767. ctx1.TransformFinalBlock(password.ReadonlyData, 0, password.Length);
  768. Array.Copy(ctx1.Hash, final, final.Length);
  769. for (int pl = password.Length; pl > 0; pl -= 16)
  770. ctx.TransformBlock(final, 0, pl > 16 ? 16 : pl, null, 0);
  771. //Clear the data
  772. for (int i = 0; i < final.Length; ++i)
  773. final[i] = 0;
  774. // "Then something really weird...", per zend PHP - what a ridiculous waste of CPU cycles
  775. byte[] zeroByte = new byte[1] { 0 };
  776. for (int i = password.Length; i != 0; i >>= 1)
  777. {
  778. if ((i & 1) != 0)
  779. ctx.TransformBlock(zeroByte, 0, 1, null, 0);
  780. else
  781. ctx.TransformBlock(password.ReadonlyData, 0, 1, null, 0);
  782. }
  783. ctx.TransformFinalBlock(ArrayUtils.EmptyBytes, 0, 0);
  784. Array.Copy(ctx.Hash, final, final.Length);
  785. /* Per md5crypt.c, again ridiculous but we want to keep consistent "
  786. * And now, just to make sure things don't run too fast. On a 60 MHz
  787. * Pentium this takes 34 msec, so you would need 30 seconds to build
  788. * a 1000 entry dictionary... "
  789. */
  790. for (int i = 0; i < 1000; ++i)
  791. {
  792. ctx1.Initialize();
  793. if ((i & 1) != 0)
  794. ctx1.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
  795. else
  796. ctx1.TransformBlock(final, 0, final.Length, null, 0);
  797. if ((i % 3) != 0)
  798. ctx1.TransformBlock(key, startOffset, keyLength, null, 0);
  799. if ((i % 7) != 0)
  800. ctx1.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
  801. if ((i & 1) != 0)
  802. ctx1.TransformFinalBlock(final, 0, final.Length);
  803. else
  804. ctx1.TransformFinalBlock(password.ReadonlyData, 0, password.Length);
  805. Array.Copy(ctx1.Hash, final, final.Length);
  806. }
  807. to64(result, ((UInt32)final[0] << 16) | ((UInt32)final[6] << 8) | (UInt32)final[12], 4);
  808. to64(result, ((UInt32)final[1] << 16) | ((UInt32)final[7] << 8) | (UInt32)final[13], 4);
  809. to64(result, ((UInt32)final[2] << 16) | ((UInt32)final[8] << 8) | (UInt32)final[14], 4);
  810. to64(result, ((UInt32)final[3] << 16) | ((UInt32)final[9] << 8) | (UInt32)final[15], 4);
  811. to64(result, ((UInt32)final[4] << 16) | ((UInt32)final[10] << 8) | (UInt32)final[5], 4);
  812. to64(result, (UInt32)final[11], 2);
  813. return new PhpBytes(result.ToArray());
  814. }
  815. /// <summary>
  816. /// Encrypts a string (one-way) with given key.
  817. /// </summary>
  818. /// <param name="str">The string of bytes to encrypt</param>
  819. /// <param name="salt">The key.</param>
  820. /// <returns>The encrypted string.</returns>
  821. [ImplementsFunction("crypt")]
  822. public static PhpBytes Encrypt(PhpBytes str, PhpBytes salt)
  823. {
  824. if (str == null) str = PhpBytes.Empty;
  825. Stream stream = new System.IO.MemoryStream(str.ReadonlyData);
  826. bool usemd5 = (salt == null) || (salt.Length == 0) || ByteArrayEquals(salt.ReadonlyData, MD5MagicString, MD5MagicString.Length);
  827. int requiredKeyLength = usemd5 ? MaxMD5Key : MaxDESKey;
  828. byte[] key = new byte[requiredKeyLength];
  829. int saltLength = requiredKeyLength;
  830. DES des = new DESCryptoServiceProvider();
  831. // prepare the key if salt is provided:
  832. if ((salt != null) && (salt.Length > 0))
  833. {
  834. //Fill with $'s first, same as zend PHP
  835. Array.Copy(System.Text.Encoding.ASCII.GetBytes(new String('$', requiredKeyLength)), key, requiredKeyLength);
  836. saltLength = System.Math.Min(requiredKeyLength, salt.Length);
  837. Array.Copy(salt.ReadonlyData, key, saltLength);
  838. }
  839. else
  840. Array.Copy(des.Key, key, InternalMD5Key); //Random 8-byte sequence
  841. if (usemd5)
  842. {
  843. return DoMD5Password(key, str);
  844. }
  845. else
  846. {
  847. MemoryStream result = new MemoryStream();
  848. des.IV = new byte[8];
  849. des.Key = key;
  850. ICryptoTransform transform = des.CreateEncryptor(des.Key, des.IV);
  851. CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Read);
  852. // PHP puts the relevant salt characters in the beginning
  853. result.Write(key, 0, saltLength);
  854. byte[] buffer = new byte[256];
  855. int rd;
  856. while ((rd = cs.Read(buffer, 0, buffer.Length)) > 0)
  857. {
  858. int i;
  859. for (i = 0; i < rd; ++i)
  860. {
  861. switch (i % 3)
  862. {
  863. case 0:
  864. result.WriteByte(itoa64[buffer[i] >> 2]);
  865. break;
  866. case 1:
  867. result.WriteByte(itoa64[((buffer[i - 1] & 0x3) << 4) | (buffer[i] >> 4)]);
  868. break;
  869. case 2:
  870. result.WriteByte(itoa64[((buffer[i - 1] & 0xF) << 2) | (buffer[i] >> 6)]);
  871. result.WriteByte(itoa64[buffer[i] & 0x3F]);
  872. break;
  873. }
  874. }
  875. //Leftover bits
  876. switch (i % 3)
  877. {
  878. case 1:
  879. result.WriteByte(itoa64[((buffer[i - 1] & 0x3) << 4)]);
  880. break;
  881. case 2:
  882. result.WriteByte(itoa64[((buffer[i - 1] & 0xF) << 2)]);
  883. break;
  884. }
  885. }
  886. return new PhpBytes(result.ToArray());
  887. }
  888. }
  889. #endif
  890. #endregion
  891. #endregion
  892. #region strrev, strspn, strcspn
  893. /// <summary>
  894. /// Reverses the given string.
  895. /// </summary>
  896. /// <param name="obj">The string to be reversed.</param>
  897. /// <returns>The reversed string or empty string if <paramref name="obj"/> is null.</returns>
  898. [ImplementsFunction("strrev")]
  899. [PureFunction]
  900. public static object Reverse(object obj)
  901. {
  902. PhpBytes bytes;
  903. if ((bytes = obj as PhpBytes) != null)
  904. {
  905. return Reverse(bytes);
  906. }
  907. else
  908. {
  909. return Reverse(PHP.Core.Convert.ObjectToString(obj));
  910. }
  911. }
  912. internal static PhpBytes Reverse(PhpBytes bytes)
  913. {
  914. int length;
  915. if ((length = bytes.Length) == 0)
  916. return PhpBytes.Empty;
  917. byte[] reversed = new byte[length];
  918. byte[] data = bytes.ReadonlyData;
  919. for (int i = 0, j = length - 1; j >= 0; j--, i++)
  920. reversed[i] = data[j];
  921. return new PhpBytes(reversed);
  922. }
  923. internal static string Reverse(string str)
  924. {
  925. if (String.IsNullOrEmpty(str))
  926. return String.Empty;
  927. int length = str.Length;
  928. StringBuilder result = new StringBuilder(length, length);
  929. result.Length = length;
  930. for (int i = 0, j = length - 1; j >= 0; j--, i++)
  931. result[i] = str[j];
  932. return result.ToString();
  933. }
  934. /// <summary>
  935. /// Finds a length of an initial segment consisting entirely of specified characters.
  936. /// </summary>
  937. /// <param name="str">The string to be searched in.</param>
  938. /// <param name="acceptedChars">Accepted characters.</param>
  939. /// <returns>
  940. /// The length of the initial segment consisting entirely of characters in <paramref name="acceptedChars"/>
  941. /// or zero if any argument is null.
  942. /// </returns>
  943. [ImplementsFunction("strspn")]
  944. public static int StrSpn(string str, string acceptedChars)
  945. {
  946. return StrSpnInternal(str, acceptedChars, 0, int.MaxValue, false);
  947. }
  948. /// <summary>
  949. /// Finds a length of a segment consisting entirely of specified characters.
  950. /// </summary>
  951. /// <param name="str">The string to be searched in.</param>
  952. /// <param name="acceptedChars">Accepted characters.</param>
  953. /// <param name="offset">The relativized offset of the first item of the slice.</param>
  954. /// <returns>
  955. /// The length of the substring consisting entirely of characters in <paramref name="acceptedChars"/> or
  956. /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
  957. /// (see <see cref="PhpMath.AbsolutizeRange"/> where <c>length</c> is infinity).
  958. /// </returns>
  959. [ImplementsFunction("strspn")]
  960. public static int StrSpn(string str, string acceptedChars, int offset)
  961. {
  962. return StrSpnInternal(str, acceptedChars, offset, int.MaxValue, false);
  963. }
  964. /// <summary>
  965. /// Finds a length of a segment consisting entirely of specified characters.
  966. /// </summary>
  967. /// <param name="str">The string to be searched in.</param>
  968. /// <param name="acceptedChars">Accepted characters.</param>
  969. /// <param name="offset">The relativized offset of the first item of the slice.</param>
  970. /// <param name="length">The relativized length of the slice.</param>
  971. /// <returns>
  972. /// The length of the substring consisting entirely of characters in <paramref name="acceptedChars"/> or
  973. /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
  974. /// (see <see cref="PhpMath.AbsolutizeRange"/> and takes at most absolutized <paramref name="length"/> characters.
  975. /// </returns>
  976. [ImplementsFunction("strspn")]
  977. public static int StrSpn(string str, string acceptedChars, int offset, int length)
  978. {
  979. return StrSpnInternal(str, acceptedChars, offset, length, false);
  980. }
  981. /// <summary>
  982. /// Finds a length of an initial segment consisting entirely of any characters excpept for specified ones.
  983. /// </summary>
  984. /// <param name="str">The string to be searched in.</param>
  985. /// <param name="acceptedChars">Accepted characters.</param>
  986. /// <returns>
  987. /// The length of the initial segment consisting entirely of characters not in <paramref name="acceptedChars"/>
  988. /// or zero if any argument is null.
  989. /// </returns>
  990. [ImplementsFunction("strcspn")]
  991. public static int StrCSpn(string str, string acceptedChars)
  992. {
  993. return StrSpnInternal(str, acceptedChars, 0, int.MaxValue, true);
  994. }
  995. /// <summary>
  996. /// Finds a length of a segment consisting entirely of any characters excpept for specified ones.
  997. /// </summary>
  998. /// <param name="str">The string to be searched in.</param>
  999. /// <param name="acceptedChars">Accepted characters.</param>
  1000. /// <param name="offset">The relativized offset of the first item of the slice.</param>
  1001. /// <returns>
  1002. /// The length of the substring consisting entirely of characters not in <paramref name="acceptedChars"/> or
  1003. /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
  1004. /// (see <see cref="PhpMath.AbsolutizeRange"/> where <c>length</c> is infinity).
  1005. /// </returns>
  1006. [ImplementsFunction("strcspn")]
  1007. public static int StrCSpn(string str, string acceptedChars, int offset)
  1008. {
  1009. return StrSpnInternal(str, acceptedChars, offset, int.MaxValue, true);
  1010. }
  1011. /// <summary>
  1012. /// Finds a length of a segment consisting entirely of any characters except for specified ones.
  1013. /// </summary>
  1014. /// <param name="str">The string to be searched in.</param>
  1015. /// <param name="acceptedChars">Accepted characters.</param>
  1016. /// <param name="offset">The relativized offset of the first item of the slice.</param>
  1017. /// <param name="length">The relativized length of the slice.</param>
  1018. /// <returns>
  1019. /// The length of the substring consisting entirely of characters not in <paramref name="acceptedChars"/> or
  1020. /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
  1021. /// (see <see cref="PhpMath.AbsolutizeRange"/> and takes at most absolutized <paramref name="length"/> characters.
  1022. /// </returns>
  1023. [ImplementsFunction("strcspn")]
  1024. public static int StrCSpn(string str, string acceptedChars, int offset, int length)
  1025. {
  1026. return StrSpnInternal(str, acceptedChars, offset, length, true);
  1027. }
  1028. /// <summary>
  1029. /// Internal version of <see cref="StrSpn"/> (complement off) and <see cref="StrCSpn"/> (complement on).
  1030. //…

Large files files are truncated, but you can click here to view the full file