/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
- /*
-
- Copyright (c) 2004-2006 Tomas Matousek and Ladislav Prosek.
-
- The use and distribution terms for this software are contained in the file named License.txt,
- which can be found in the root of the Phalanger distribution. By using this software
- in any fashion, you are agreeing to be bound by the terms of this license.
-
- You must not remove this notice from this software.
-
- */
-
- /*
- GENERICS:
- Generic char map and hashtable will allow to handle all Unicode characters and get rid of the errors:
- <exception cref="PhpException"><paramref name="whiteSpaceCharacters"/> contains Unicode characters greater than '\u0800'.</exception>
-
- TODO:
- - PHP6 - new functions hash($alg,...) hash_file($alg, ...)
- - Added overflow checks to wordwrap() function. (5.1.3)
- - Fixed offset/length parameter validation in substr_compare() function. (5.1.3)
- - (strncmp & strncasecmp do not return false on negative string length). (5.1.3)
-
- */
- using System;
- using PHP.Core;
- using System.IO;
- using System.Text;
- using System.Collections;
- using System.Collections.Generic;
- using System.Security.Cryptography;
- using System.Text.RegularExpressions;
- using System.ComponentModel;
-
- #if SILVERLIGHT
- using PHP.CoreCLR;
- using System.Windows.Browser;
- #else
- using System.Web;
- #endif
-
- namespace PHP.Library
- {
- #region Enumerations
-
- /// <summary>Quote conversion options.</summary>
- [Flags]
- public enum QuoteStyle
- {
- /// <summary>
- /// Default quote style for <c>htmlentities</c>.
- /// </summary>
- HtmlEntitiesDefault = QuoteStyle.Compatible | QuoteStyle.Html401,
-
- /// <summary>Single quotes.</summary>
- SingleQuotes = 1,
-
- /// <summary>Double quotes.</summary>
- DoubleQuotes = 2,
-
- /// <summary>
- /// No quotes.
- /// Will leave both double and single quotes unconverted.
- /// </summary>
- [ImplementsConstant("ENT_NOQUOTES")]
- NoQuotes = 0,
-
- /// <summary>
- /// Will convert double-quotes and leave single-quotes alone.
- /// </summary>
- [ImplementsConstant("ENT_COMPAT")]
- Compatible = DoubleQuotes,
-
- /// <summary>
- /// Both single and double quotes.
- /// Will convert both double and single quotes.
- /// </summary>
- [ImplementsConstant("ENT_QUOTES")]
- BothQuotes = DoubleQuotes | SingleQuotes,
-
- /// <summary>
- /// Silently discard invalid code unit sequences instead of
- /// returning an empty string. Using this flag is discouraged
- /// as it may have security implications.
- /// </summary>
- [ImplementsConstant("ENT_IGNORE")]
- Ignore = 4,
-
- /// <summary>
- /// Replace invalid code unit sequences with a Unicode
- /// Replacement Character U+FFFD (UTF-8) or &#FFFD;
- /// (otherwise) instead of returning an empty string.
- /// </summary>
- [ImplementsConstant("ENT_SUBSTITUTE")] // 8
- Substitute = 8,
- /// <summary>
- /// Handle code as HTML 4.01.
- /// </summary>
- [ImplementsConstant("ENT_HTML401")] // 0
- Html401 = NoQuotes,
- /// <summary>
- /// Handle code as XML 1.
- /// </summary>
- [ImplementsConstant("ENT_XML1")] // 16
- XML1 = 16,
- /// <summary>
- /// Handle code as XHTML.
- /// </summary>
- [ImplementsConstant("ENT_XHTML")] // 32
- XHTML = 32,
- /// <summary>
- /// Handle code as HTML 5.
- /// </summary>
- [ImplementsConstant("ENT_HTML5")] // (16|32)
- HTML5 = XML1 | XHTML,
- /// <summary>
- /// Replace invalid code points for the given document type
- /// with a Unicode Replacement Character U+FFFD (UTF-8) or &#FFFD;
- /// (otherwise) instead of leaving them as is.
- /// This may be useful, for instance, to ensure the well-formedness
- /// of XML documents with embedded external content.
- /// </summary>
- [ImplementsConstant("ENT_DISALLOWED")] // 128
- Disallowed = 128,
- };
-
- /// <summary>Types of HTML entities tables.</summary>
- public enum HtmlEntitiesTable
- {
- /// <summary>Table containing special characters only.</summary>
- [ImplementsConstant("HTML_SPECIALCHARS")]
- SpecialChars = 0,
-
- /// <summary>Table containing all entities.</summary>
- [ImplementsConstant("HTML_ENTITIES")]
- AllEntities = 1
- };
-
- /// <summary>
- /// Type of padding.
- /// </summary>
- public enum PaddingType
- {
- /// <summary>Pad a string from the left.</summary>
- [ImplementsConstant("STR_PAD_LEFT")]
- Left = 0,
-
- /// <summary>Pad a string from the right.</summary>
- [ImplementsConstant("STR_PAD_RIGHT")]
- Right = 1,
-
- /// <summary>Pad a string from both sides.</summary>
- [ImplementsConstant("STR_PAD_BOTH")]
- Both = 2
- }
-
- /// <summary>
- /// Format of a return value of <see cref="PhpStrings.CountWords"/> method. Constants are not named in PHP.
- /// </summary>
- public enum WordCountResult
- {
- /// <summary>
- /// Return number of words in string.
- /// </summary>
- WordCount = 0,
-
- /// <summary>
- /// Return array of words.
- /// </summary>
- WordsArray = 1,
-
- /// <summary>
- /// Return positions to words mapping.
- /// </summary>
- PositionsToWordsMapping = 2
- }
-
- #endregion
-
- /// <summary>
- /// Manipulates strings.
- /// </summary>
- /// <threadsafety static="true"/>
- public static class PhpStrings
- {
- #region Character map
-
- #if !SILVERLIGHT
- [ThreadStatic]
- #endif
- private static CharMap _charmap;
-
- /// <summary>
- /// Get clear <see cref="CharMap"/> to be used by current thread. <see cref="_charmap"/>.
- /// </summary>
- internal static CharMap InitializeCharMap()
- {
- CharMap result = _charmap;
-
- if (result == null)
- _charmap = result = new CharMap(0x0800);
- else
- result.ClearAll();
-
- return result;
- }
-
- #endregion
-
-
- #region Binary Data Functions
-
- #region ord, chr, bin2hex, ord_unicode, chr_unicode, bin2hex_unicode, to_binary
-
- /// <summary>
- /// Returns ASCII code of the first character of a string of bytes.
- /// </summary>
- /// <param name="bytes">The string of bytes which the first byte will be returned.</param>
- /// <returns>The ASCII code of <paramref name="bytes"/>[0] or zero if null or empty.</returns>
- [ImplementsFunction("ord")]
- [PureFunction]
- public static int Ord(PhpBytes bytes)
- {
- return (bytes == null || bytes.Length == 0) ? 0 : (int)bytes[0];
- }
-
- /// <summary>
- /// Returns Unicode ordinal number of the first character of a string.
- /// </summary>
- /// <param name="str">The string which the first character's ordinal number is returned.</param>
- /// <returns>The ordinal number of <paramref name="str"/>[0].</returns>
- [ImplementsFunction("ord_unicode")]
- [PureFunction]
- public static int OrdUnicode(string str)
- {
- return (str == null || str == String.Empty) ? 0 : (int)str[0];
- }
-
- /// <summary>
- /// Converts ordinal number of character to a binary string containing that character.
- /// </summary>
- /// <param name="charCode">The ASCII code.</param>
- /// <returns>The character with <paramref name="charCode"/> ASCIT code.</returns>
- /// <remarks>Current code-page is determined by the <see cref="ApplicationConfiguration.GlobalizationSection.PageEncoding"/> property.</remarks>
- [ImplementsFunction("chr")]
- [PureFunction]
- public static PhpBytes Chr(int charCode)
- {
- return new PhpBytes(unchecked((byte)charCode));
- }
-
- /// <summary>
- /// Converts ordinal number of Unicode character to a string containing that character.
- /// </summary>
- /// <param name="charCode">The ordinal number of character.</param>
- /// <returns>The character with <paramref name="charCode"/> ordnial number.</returns>
- [ImplementsFunction("chr_unicode")]
- [PureFunction]
- public static string ChrUnicode(int charCode)
- {
- return unchecked((char)charCode).ToString();
- }
-
- /// <summary>
- /// Converts a string of bytes into hexadecimal representation.
- /// </summary>
- /// <param name="bytes">The string of bytes.</param>
- /// <returns>Concatenation of hexadecimal values of bytes of <paramref name="bytes"/>.</returns>
- /// <example>
- /// The string "01A" is converted into string "303140" because ord('0') = 0x30, ord('1') = 0x31, ord('A') = 0x40.
- /// </example>
- [ImplementsFunction("bin2hex")]
- [PureFunction]
- public static string BinToHex(PhpBytes bytes)
- {
- return (bytes == null) ? String.Empty : StringUtils.BinToHex(bytes.ReadonlyData, null);
- }
-
- /// <summary>
- /// Converts a string into hexadecimal representation.
- /// </summary>
- /// <param name="str">The string to be converted.</param>
- /// <returns>
- /// The concatenated four-characters long hexadecimal numbers each representing one character of <paramref name="str"/>.
- /// </returns>
- [ImplementsFunction("bin2hex_unicode")]
- [PureFunction]
- public static string BinToHex(string str)
- {
- if (str == null) return null;
-
- int length = str.Length;
- StringBuilder result = new StringBuilder(length * 4, length * 4);
- result.Length = length * 4;
-
- const string hex_digs = "0123456789abcdef";
-
- for (int i = 0; i < length; i++)
- {
- int c = (int)str[i];
- result[4 * i + 0] = hex_digs[(c & 0xf000) >> 12];
- result[4 * i + 1] = hex_digs[(c & 0x0f00) >> 8];
- result[4 * i + 2] = hex_digs[(c & 0x00f0) >> 4];
- result[4 * i + 3] = hex_digs[(c & 0x000f)];
- }
-
- return result.ToString();
- }
-
- /// <summary>
- /// Converts a variable to a string of binary data.
- /// </summary>
- /// <param name="var">A variable.</param>
- /// <returns>Binary data.</returns>
- [ImplementsFunction("to_binary")]
- [PureFunction]
- public static PhpBytes ToBinary(PhpBytes var)
- {
- return var;
- }
-
- #endregion
-
-
- #region convert_cyr_string
-
- #region cyrWin1251 (1251), cyrCp866 (20866), cyrIso88595 (28595), cyrMac (10007) conversion tables
-
- /// <summary>
- /// Cyrillic translation table for Windows CP1251 character set.
- /// </summary>
- private static readonly byte[] cyrWin1251 = new byte[]
- {
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,
- 46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,46,
- 154,174,190,46,159,189,46,46,179,191,180,157,46,46,156,183,
- 46,46,182,166,173,46,46,158,163,152,164,155,46,46,46,167,
- 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
- 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
- 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
- 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209,
-
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,32,32,184,186,32,179,191,32,32,32,32,32,180,162,32,
- 32,32,32,168,170,32,178,175,32,32,32,32,32,165,161,169,
- 254,224,225,246,228,229,244,227,245,232,233,234,235,236,237,238,
- 239,255,240,241,242,243,230,226,252,251,231,248,253,249,247,250,
- 222,192,193,214,196,197,212,195,213,200,201,202,203,204,205,206,
- 207,223,208,209,210,211,198,194,220,219,199,216,221,217,215,218,
- };
-
- /// <summary>
- /// Cyrillic translation table for CP866 character set.
- /// </summary>
- private static readonly byte[] cyrCp866 = new byte[]
- {
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
- 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
- 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
- 35,35,35,124,124,124,124,43,43,124,124,43,43,43,43,43,
- 43,45,45,124,45,43,124,124,43,43,45,45,124,45,43,45,
- 45,45,45,43,43,43,43,43,43,43,43,35,35,124,124,35,
- 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209,
- 179,163,180,164,183,167,190,174,32,149,158,32,152,159,148,154,
-
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 205,186,213,241,243,201,32,245,187,212,211,200,190,32,247,198,
- 199,204,181,240,242,185,32,244,203,207,208,202,216,32,246,32,
- 238,160,161,230,164,165,228,163,229,168,169,170,171,172,173,174,
- 175,239,224,225,226,227,166,162,236,235,167,232,237,233,231,234,
- 158,128,129,150,132,133,148,131,149,136,137,138,139,140,141,142,
- 143,159,144,145,146,147,134,130,156,155,135,152,157,153,151,154,
- };
-
- /// <summary>
- /// Cyrillic translation table for ISO88595 character set.
- /// </summary>
- private static readonly byte[] cyrIso88595 = new byte[]
- {
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,179,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
- 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
- 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
- 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209,
- 32,163,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
-
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,32,32,241,32,32,32,32,32,32,32,32,32,32,32,32,
- 32,32,32,161,32,32,32,32,32,32,32,32,32,32,32,32,
- 238,208,209,230,212,213,228,211,229,216,217,218,219,220,221,222,
- 223,239,224,225,226,227,214,210,236,235,215,232,237,233,231,234,
- 206,176,177,198,180,181,196,179,197,184,185,186,187,188,189,190,
- 191,207,192,193,194,195,182,178,204,203,183,200,205,201,199,202,
- };
-
- /// <summary>
- /// Cyrillic translation table for Mac character set.
- /// </summary>
- private static readonly byte[] cyrMac = new byte[]
- {
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
- 242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
- 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
- 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
- 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
- 144,145,146,147,148,149,150,151,152,153,154,155,156,179,163,209,
- 193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
- 210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,255,
-
- 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
- 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
- 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
- 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
- 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
- 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
- 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
-
- 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
- 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
- 160,161,162,222,164,165,166,167,168,169,170,171,172,173,174,175,
- 176,177,178,221,180,181,182,183,184,185,186,187,188,189,190,191,
- 254,224,225,246,228,229,244,227,245,232,233,234,235,236,237,238,
- 239,223,240,241,242,243,230,226,252,251,231,248,253,249,247,250,
- 158,128,129,150,132,133,148,131,149,136,137,138,139,140,141,142,
- 143,159,144,145,146,147,134,130,156,155,135,152,157,153,151,154,
- };
-
- #endregion
-
- /// <summary>
- /// Returns a Cyrillic translation table for a specified character set,
- /// </summary>
- /// <param name="code">The character set code. Can be one of 'k', 'w', 'i', 'a', 'd', 'm'.</param>
- /// <returns>The translation table or null if no table is associated with given charset code.</returns>
- internal static byte[] GetCyrTableInternal(char code)
- {
- switch (Char.ToUpper(code))
- {
- case 'W':
- return cyrWin1251;
-
- case 'A':
- case 'D':
- return cyrCp866;
-
- case 'I':
- return cyrIso88595;
-
- case 'M':
- return cyrMac;
-
- case 'K':
- return null;
-
- default:
- return ArrayUtils.EmptyBytes;
- }
- }
-
- /// <include file='Doc/Strings.xml' path='docs/method[@name="ConvertCyrillic"]/*'/>
- /// <exception cref="PhpException">Thrown if source or destination charset is invalid. </exception>
- [ImplementsFunction("convert_cyr_string")]
- public static PhpBytes ConvertCyrillic(PhpBytes bytes, string srcCharset, string dstCharset)
- {
- if (bytes == null) return null;
- if (bytes.Length == 0) return PhpBytes.Empty;
-
- // checks srcCharset argument:
- if (srcCharset == null || srcCharset == String.Empty)
- {
- PhpException.InvalidArgument("srcCharset", LibResources.GetString("arg:null_or_empty"));
- return PhpBytes.Empty;
- }
-
- // checks dstCharset argument:
- if (dstCharset == null || dstCharset == String.Empty)
- {
- PhpException.InvalidArgument("dstCharset", LibResources.GetString("arg:null_or_empty"));
- return PhpBytes.Empty;
- }
-
- // get and check source charset table:
- byte[] fromTable = GetCyrTableInternal(srcCharset[0]);
- if (fromTable != null && fromTable.Length < 256)
- {
- PhpException.Throw(PhpError.Warning, LibResources.GetString("invalid_src_charser"));
- return PhpBytes.Empty;
- }
-
- // get and check destination charset table:
- byte[] toTable = GetCyrTableInternal(dstCharset[0]);
- if (toTable != null && toTable.Length < 256)
- {
- PhpException.Throw(PhpError.Warning, LibResources.GetString("invalid_dst_charser"));
- return PhpBytes.Empty;
- }
-
- byte[] data = bytes.ReadonlyData;
- byte[] result = new byte[data.Length];
-
- // perform conversion:
- if (fromTable == null)
- {
- if (toTable != null)
- {
- for (int i = 0; i < data.Length; i++) result[i] = toTable[data[i] + 256];
- }
- }
- else
- {
- if (toTable == null)
- {
- for (int i = 0; i < data.Length; i++) result[i] = fromTable[data[i]];
- }
- else
- {
- for (int i = 0; i < data.Length; i++) result[i] = toTable[fromTable[data[i]] + 256];
- }
- }
-
- return new PhpBytes(result);
- }
-
- #endregion
-
-
- #region count_chars
-
- /// <summary>
- /// Creates a histogram of Unicode character occurence in the given string.
- /// </summary>
- /// <param name="str">The string to be processed.</param>
- /// <returns>The array of characters frequency (unsorted).</returns>
- [ImplementsFunction("count_chars_unicode")]
- public static PhpArray CountChars(string str)
- {
- PhpArray count = new PhpArray();
-
- for (int i = str.Length - 1; i >= 0; i--)
- {
- int j = (int)str[i];
- object c = count[j];
- count[j] = (c == null) ? 1 : (int)c + 1;
- }
-
- return count;
- }
-
- /// <summary>
- /// Creates a histogram of byte occurence in the given array of bytes.
- /// </summary>
- /// <param name="bytes">The array of bytes to be processed.</param>
- /// <returns>The array of bytes frequency.</returns>
- public static int[] CountBytes(byte[] bytes)
- {
- if (bytes == null)
- throw new ArgumentNullException("bytes");
-
- int[] count = new int[256];
-
- for (int i = bytes.Length - 1; i >= 0; i--)
- count[bytes[i]]++;
-
- return count;
- }
-
- /// <summary>
- /// Creates a histogram of byte occurrence in specified string of bytes.
- /// </summary>
- /// <param name="bytes">Bytes to be processed.</param>
- /// <returns>The array of characters frequency.</returns>
- [ImplementsFunction("count_chars")]
- public static PhpArray CountChars(PhpBytes bytes)
- {
- return (bytes == null) ? new PhpArray() : new PhpArray(CountBytes(bytes.ReadonlyData), 0, 256);
- }
-
- /// <summary>
- /// Creates a histogram of character occurence in a string or string of bytes.
- /// </summary>
- /// <param name="data">The string or bytes to be processed.</param>
- /// <param name="mode">Determines the type of result.</param>
- /// <returns>Depending on <paramref name="mode"/> the following is returned:
- /// <list type="bullet">
- /// <item><term>0</term><description>an array with the character ordinals as key and their frequency as value,</description></item>
- /// <item><term>1</term><description>same as 0 but only characters with a frequency greater than zero are listed,</description></item>
- /// <item><term>2</term><description>same as 0 but only characters with a frequency equal to zero are listed,</description></item>
- /// <item><term>3</term><description>a string containing all used characters is returned,</description></item>
- /// <item><term>4</term><description>a string containing all not used characters is returned.</description></item>
- /// </list>
- /// </returns>
- /// <exception cref="PhpException">The <paramref name="mode"/> is invalid.</exception>
- /// <exception cref="PhpException">The <paramref name="data"/> contains Unicode characters greater than '\u0800'.</exception>
- [ImplementsFunction("count_chars")]
- public static object CountChars(object data, int mode)
- {
- try
- {
- switch (mode)
- {
- case 0: return new PhpArray(CountBytes(Core.Convert.ObjectToPhpBytes(data).ReadonlyData), 0, 256);
- case 1: return new PhpArray(CountBytes(Core.Convert.ObjectToPhpBytes(data).ReadonlyData), 0, 256, 0, true);
- case 2: return new PhpArray(CountBytes(Core.Convert.ObjectToPhpBytes(data).ReadonlyData), 0, 256, 0, false);
- case 3: return GetBytesContained(Core.Convert.ObjectToPhpBytes(data), 0, 255);
- case 4: return GetBytesNotContained(Core.Convert.ObjectToPhpBytes(data), 0, 255);
- default: PhpException.InvalidArgument("mode"); return null;
- }
- }
- catch (IndexOutOfRangeException)
- {
- // thrown by char map:
- PhpException.Throw(PhpError.Warning, LibResources.GetString("too_big_unicode_character"));
- return null;
- }
- }
-
- /// <summary>
- /// Returns a <see cref="String"/> containing all characters used in the specified <see cref="String"/>.
- /// </summary>
- /// <param name="str">The string to process.</param>
- /// <param name="lower">The lower limit for returned chars.</param>
- /// <param name="upper">The upper limit for returned chars.</param>
- /// <returns>
- /// The string containing characters used in <paramref name="str"/> which are sorted according to their ordinal values.
- /// </returns>
- /// <exception cref="IndexOutOfRangeException"><paramref name="str"/> contains characters greater than '\u0800'.</exception>
- public static string GetCharactersContained(string str, char lower, char upper)
- {
- CharMap charmap = InitializeCharMap();
-
- charmap.Add(str);
- return charmap.ToString(lower, upper, false);
- }
-
- /// <summary>
- /// Returns a <see cref="String"/> containing all characters used in the specified <see cref="String"/>.
- /// </summary>
- /// <param name="str">The string to process.</param>
- /// <param name="lower">The lower limit for returned chars.</param>
- /// <param name="upper">The upper limit for returned chars.</param>
- /// <returns>
- /// The string containing characters used in <paramref name="str"/> which are sorted according to their ordinal values.
- /// </returns>
- /// <exception cref="IndexOutOfRangeException"><paramref name="str"/> contains characters greater than '\u0800'.</exception>
- public static string GetCharactersNotContained(string str, char lower, char upper)
- {
- CharMap charmap = InitializeCharMap();
-
- charmap.Add(str);
- return charmap.ToString(lower, upper, true);
- }
-
- private static BitArray CreateByteMap(PhpBytes/*!*/ bytes, out int count)
- {
- BitArray map = new BitArray(256);
- map.Length = 256;
-
- count = 0;
- for (int i = 0; i < bytes.Length; i++)
- {
- if (!map[bytes[i]])
- {
- map[bytes[i]] = true;
- count++;
- }
- }
- return map;
- }
-
- public static PhpBytes GetBytesContained(PhpBytes bytes, byte lower, byte upper)
- {
- if (bytes == null) bytes = PhpBytes.Empty;
-
- int count;
- BitArray map = CreateByteMap(bytes, out count);
-
- byte[] result = new byte[count];
- int j = 0;
- for (int i = lower; i <= upper; i++)
- {
- if (map[i]) result[j++] = (byte)i;
- }
-
- return new PhpBytes(result);
- }
-
- public static PhpBytes GetBytesNotContained(PhpBytes bytes, byte lower, byte upper)
- {
- if (bytes == null) bytes = PhpBytes.Empty;
-
- int count;
- BitArray map = CreateByteMap(bytes, out count);
-
- byte[] result = new byte[map.Length - count];
- int j = 0;
- for (int i = lower; i <= upper; i++)
- {
- if (!map[i]) result[j++] = (byte)i;
- }
-
- return new PhpBytes(result);
- }
-
- #endregion
-
-
- #region crypt (CLR only)
- #if !SILVERLIGHT
-
- /// <summary>
- /// Specifies whether standard DES algorithm is implemented.
- /// 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
- /// If you want key compatibility with PHP, use CRYPT_MD5 by passing in a key starting with "?1?"
- /// </summary>
- [ImplementsConstant("CRYPT_STD_DES")]
- public const int CryptStandardDES = 1;
-
- /// <summary>
- /// Specifies whether extended DES algorithm is implemented.
- /// </summary>
- [ImplementsConstant("CRYPT_EXT_DES")]
- public const int CryptExtendedDES = 0;
-
- /// <summary>
- /// Specifies whether MD5 algorithm is implemented.
- /// </summary>
- [ImplementsConstant("CRYPT_MD5")]
- public const int CryptMD5 = 1;
-
- /// <summary>
- /// Specifies whether Blowfish encryption is implemented.
- /// </summary>
- [ImplementsConstant("CRYPT_BLOWFISH")]
- public const int CryptBlowfish = 0;
-
- /// <summary>
- /// Specifies the length of the salt applicable to the <see cref="Encrypt"/> method.
- /// </summary>
- [ImplementsConstant("CRYPT_SALT_LENGTH")]
- public const int CryptSaltLength = 9;
-
- /// <summary>
- /// Encrypts a string (one-way) with a random key.
- /// </summary>
- /// <param name="str">The string to encrypt.</param>
- /// <returns>The encrypted string.</returns>
- [ImplementsFunction("crypt")]
- public static PhpBytes Encrypt(PhpBytes str)
- {
- return Encrypt(str, null);
- }
-
- private const int MaxMD5Key = 12;
- private const int InternalMD5Key = 8;
- private const int MaxKeyLength = MaxMD5Key;
- private const int MaxDESKey = 8;
-
- public static bool ByteArrayEquals(byte[] array1, byte[] array2, int compareLength)
- {
- // If the other object is null, of a diffent type, or
- // of an array of a different length then skip out now.
- if ((array2 == null) || (array1 == null) || (compareLength <= 0 && (array1.Length != array2.Length)))
- return false;
-
- int minArray = Math.Min(array1.Length, array2.Length);
- if (compareLength <= 0)
- compareLength = minArray;
- else
- compareLength = Math.Min(minArray, compareLength);
-
- // If any of the elements are not equal, skip out.
- for (int i = 0; i < compareLength; ++i)
- if (array1[i] != array2[i])
- return false;
-
- // They're both the same length and the elements are all
- // equal so consider the arrays to be equal.
- return true;
- }
-
- //PHP's non-standard base64 used in converting md5 binary crypt() into chars
- //0 ... 63 => ascii - 64
- //aka bin_to_ascii ((c) >= 38 ? ((c) - 38 + 'a') : (c) >= 12 ? ((c) - 12 + 'A') : (c) + '.')
- private static byte[] itoa64 = System.Text.Encoding.ASCII.GetBytes("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
-
- private static void to64(MemoryStream stream, UInt32 v, int n)
- {
- while (--n >= 0)
- {
- stream.WriteByte(itoa64[v & 0x3f]);
- v >>= 6;
- }
- }
-
- private static byte[] MD5MagicString = System.Text.Encoding.ASCII.GetBytes("$1$");
- private static PhpBytes DoMD5Password(byte[] key, PhpBytes password)
- {
- MD5CryptoServiceProvider ctx = new MD5CryptoServiceProvider(), ctx1 = new MD5CryptoServiceProvider();
- MemoryStream result = new MemoryStream();
- byte[] final = new byte[16];
-
- int startOffset = 0, endOffset = 0;
- /* If it starts with the magic string, then skip that */
- if (ByteArrayEquals(key, MD5MagicString, MD5MagicString.Length))
- startOffset += MD5MagicString.Length;
-
- /* It stops at the first '$', max InternalMD5Key chars */
- for (endOffset = startOffset; key[endOffset] != '\0' && key[endOffset] != '$' && endOffset < (startOffset + InternalMD5Key); ++endOffset)
- continue;
- int keyLength = endOffset - startOffset;
-
- // PHP puts the relevant salt characters in the beginning
- result.Write(MD5MagicString, 0, MD5MagicString.Length);
- result.Write(key, startOffset, keyLength);
- result.Write(System.Text.Encoding.ASCII.GetBytes(new char[] { '$' }), 0, 1);
-
- ctx.Initialize();
- /* The password first, since that is what is most unknown */
- ctx.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
- ctx.TransformBlock(MD5MagicString, 0, MD5MagicString.Length, null, 0);
- ctx.TransformBlock(key, startOffset, keyLength, null, 0);
-
- ctx1.Initialize();
- /* Then just as many characters of the MD5(pw,salt,pw) */
- ctx1.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
- ctx1.TransformBlock(key, startOffset, keyLength, null, 0);
- ctx1.TransformFinalBlock(password.ReadonlyData, 0, password.Length);
- Array.Copy(ctx1.Hash, final, final.Length);
-
- for (int pl = password.Length; pl > 0; pl -= 16)
- ctx.TransformBlock(final, 0, pl > 16 ? 16 : pl, null, 0);
-
- //Clear the data
- for (int i = 0; i < final.Length; ++i)
- final[i] = 0;
-
- // "Then something really weird...", per zend PHP - what a ridiculous waste of CPU cycles
- byte[] zeroByte = new byte[1] { 0 };
- for (int i = password.Length; i != 0; i >>= 1)
- {
- if ((i & 1) != 0)
- ctx.TransformBlock(zeroByte, 0, 1, null, 0);
- else
- ctx.TransformBlock(password.ReadonlyData, 0, 1, null, 0);
- }
-
- ctx.TransformFinalBlock(ArrayUtils.EmptyBytes, 0, 0);
- Array.Copy(ctx.Hash, final, final.Length);
-
- /* Per md5crypt.c, again ridiculous but we want to keep consistent "
- * And now, just to make sure things don't run too fast. On a 60 MHz
- * Pentium this takes 34 msec, so you would need 30 seconds to build
- * a 1000 entry dictionary... "
- */
- for (int i = 0; i < 1000; ++i)
- {
- ctx1.Initialize();
-
- if ((i & 1) != 0)
- ctx1.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
- else
- ctx1.TransformBlock(final, 0, final.Length, null, 0);
-
- if ((i % 3) != 0)
- ctx1.TransformBlock(key, startOffset, keyLength, null, 0);
-
- if ((i % 7) != 0)
- ctx1.TransformBlock(password.ReadonlyData, 0, password.Length, null, 0);
-
- if ((i & 1) != 0)
- ctx1.TransformFinalBlock(final, 0, final.Length);
- else
- ctx1.TransformFinalBlock(password.ReadonlyData, 0, password.Length);
-
- Array.Copy(ctx1.Hash, final, final.Length);
- }
-
- to64(result, ((UInt32)final[0] << 16) | ((UInt32)final[6] << 8) | (UInt32)final[12], 4);
- to64(result, ((UInt32)final[1] << 16) | ((UInt32)final[7] << 8) | (UInt32)final[13], 4);
- to64(result, ((UInt32)final[2] << 16) | ((UInt32)final[8] << 8) | (UInt32)final[14], 4);
- to64(result, ((UInt32)final[3] << 16) | ((UInt32)final[9] << 8) | (UInt32)final[15], 4);
- to64(result, ((UInt32)final[4] << 16) | ((UInt32)final[10] << 8) | (UInt32)final[5], 4);
- to64(result, (UInt32)final[11], 2);
-
- return new PhpBytes(result.ToArray());
- }
-
- /// <summary>
- /// Encrypts a string (one-way) with given key.
- /// </summary>
- /// <param name="str">The string of bytes to encrypt</param>
- /// <param name="salt">The key.</param>
- /// <returns>The encrypted string.</returns>
- [ImplementsFunction("crypt")]
- public static PhpBytes Encrypt(PhpBytes str, PhpBytes salt)
- {
- if (str == null) str = PhpBytes.Empty;
-
- Stream stream = new System.IO.MemoryStream(str.ReadonlyData);
-
- bool usemd5 = (salt == null) || (salt.Length == 0) || ByteArrayEquals(salt.ReadonlyData, MD5MagicString, MD5MagicString.Length);
- int requiredKeyLength = usemd5 ? MaxMD5Key : MaxDESKey;
-
- byte[] key = new byte[requiredKeyLength];
- int saltLength = requiredKeyLength;
-
- DES des = new DESCryptoServiceProvider();
-
- // prepare the key if salt is provided:
- if ((salt != null) && (salt.Length > 0))
- {
- //Fill with $'s first, same as zend PHP
- Array.Copy(System.Text.Encoding.ASCII.GetBytes(new String('$', requiredKeyLength)), key, requiredKeyLength);
-
- saltLength = System.Math.Min(requiredKeyLength, salt.Length);
- Array.Copy(salt.ReadonlyData, key, saltLength);
- }
- else
- Array.Copy(des.Key, key, InternalMD5Key); //Random 8-byte sequence
-
- if (usemd5)
- {
- return DoMD5Password(key, str);
- }
- else
- {
- MemoryStream result = new MemoryStream();
- des.IV = new byte[8];
- des.Key = key;
-
- ICryptoTransform transform = des.CreateEncryptor(des.Key, des.IV);
- CryptoStream cs = new CryptoStream(stream, transform, CryptoStreamMode.Read);
-
- // PHP puts the relevant salt characters in the beginning
- result.Write(key, 0, saltLength);
-
- byte[] buffer = new byte[256];
- int rd;
-
- while ((rd = cs.Read(buffer, 0, buffer.Length)) > 0)
- {
- int i;
- for (i = 0; i < rd; ++i)
- {
- switch (i % 3)
- {
- case 0:
- result.WriteByte(itoa64[buffer[i] >> 2]);
- break;
- case 1:
- result.WriteByte(itoa64[((buffer[i - 1] & 0x3) << 4) | (buffer[i] >> 4)]);
- break;
- case 2:
- result.WriteByte(itoa64[((buffer[i - 1] & 0xF) << 2) | (buffer[i] >> 6)]);
- result.WriteByte(itoa64[buffer[i] & 0x3F]);
- break;
- }
- }
- //Leftover bits
- switch (i % 3)
- {
- case 1:
- result.WriteByte(itoa64[((buffer[i - 1] & 0x3) << 4)]);
- break;
- case 2:
- result.WriteByte(itoa64[((buffer[i - 1] & 0xF) << 2)]);
- break;
- }
- }
-
- return new PhpBytes(result.ToArray());
- }
- }
-
- #endif
- #endregion
-
- #endregion
-
-
- #region strrev, strspn, strcspn
-
- /// <summary>
- /// Reverses the given string.
- /// </summary>
- /// <param name="obj">The string to be reversed.</param>
- /// <returns>The reversed string or empty string if <paramref name="obj"/> is null.</returns>
- [ImplementsFunction("strrev")]
- [PureFunction]
- public static object Reverse(object obj)
- {
- PhpBytes bytes;
- if ((bytes = obj as PhpBytes) != null)
- {
- return Reverse(bytes);
- }
- else
- {
- return Reverse(PHP.Core.Convert.ObjectToString(obj));
- }
- }
-
- internal static PhpBytes Reverse(PhpBytes bytes)
- {
- int length;
- if ((length = bytes.Length) == 0)
- return PhpBytes.Empty;
-
- byte[] reversed = new byte[length];
- byte[] data = bytes.ReadonlyData;
-
- for (int i = 0, j = length - 1; j >= 0; j--, i++)
- reversed[i] = data[j];
-
- return new PhpBytes(reversed);
- }
-
- internal static string Reverse(string str)
- {
- if (String.IsNullOrEmpty(str))
- return String.Empty;
-
- int length = str.Length;
- StringBuilder result = new StringBuilder(length, length);
- result.Length = length;
-
- for (int i = 0, j = length - 1; j >= 0; j--, i++)
- result[i] = str[j];
-
- return result.ToString();
- }
-
- /// <summary>
- /// Finds a length of an initial segment consisting entirely of specified characters.
- /// </summary>
- /// <param name="str">The string to be searched in.</param>
- /// <param name="acceptedChars">Accepted characters.</param>
- /// <returns>
- /// The length of the initial segment consisting entirely of characters in <paramref name="acceptedChars"/>
- /// or zero if any argument is null.
- /// </returns>
- [ImplementsFunction("strspn")]
- public static int StrSpn(string str, string acceptedChars)
- {
- return StrSpnInternal(str, acceptedChars, 0, int.MaxValue, false);
- }
-
- /// <summary>
- /// Finds a length of a segment consisting entirely of specified characters.
- /// </summary>
- /// <param name="str">The string to be searched in.</param>
- /// <param name="acceptedChars">Accepted characters.</param>
- /// <param name="offset">The relativized offset of the first item of the slice.</param>
- /// <returns>
- /// The length of the substring consisting entirely of characters in <paramref name="acceptedChars"/> or
- /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
- /// (see <see cref="PhpMath.AbsolutizeRange"/> where <c>length</c> is infinity).
- /// </returns>
- [ImplementsFunction("strspn")]
- public static int StrSpn(string str, string acceptedChars, int offset)
- {
- return StrSpnInternal(str, acceptedChars, offset, int.MaxValue, false);
- }
-
- /// <summary>
- /// Finds a length of a segment consisting entirely of specified characters.
- /// </summary>
- /// <param name="str">The string to be searched in.</param>
- /// <param name="acceptedChars">Accepted characters.</param>
- /// <param name="offset">The relativized offset of the first item of the slice.</param>
- /// <param name="length">The relativized length of the slice.</param>
- /// <returns>
- /// The length of the substring consisting entirely of characters in <paramref name="acceptedChars"/> or
- /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
- /// (see <see cref="PhpMath.AbsolutizeRange"/> and takes at most absolutized <paramref name="length"/> characters.
- /// </returns>
- [ImplementsFunction("strspn")]
- public static int StrSpn(string str, string acceptedChars, int offset, int length)
- {
- return StrSpnInternal(str, acceptedChars, offset, length, false);
- }
-
- /// <summary>
- /// Finds a length of an initial segment consisting entirely of any characters excpept for specified ones.
- /// </summary>
- /// <param name="str">The string to be searched in.</param>
- /// <param name="acceptedChars">Accepted characters.</param>
- /// <returns>
- /// The length of the initial segment consisting entirely of characters not in <paramref name="acceptedChars"/>
- /// or zero if any argument is null.
- /// </returns>
- [ImplementsFunction("strcspn")]
- public static int StrCSpn(string str, string acceptedChars)
- {
- return StrSpnInternal(str, acceptedChars, 0, int.MaxValue, true);
- }
-
- /// <summary>
- /// Finds a length of a segment consisting entirely of any characters excpept for specified ones.
- /// </summary>
- /// <param name="str">The string to be searched in.</param>
- /// <param name="acceptedChars">Accepted characters.</param>
- /// <param name="offset">The relativized offset of the first item of the slice.</param>
- /// <returns>
- /// The length of the substring consisting entirely of characters not in <paramref name="acceptedChars"/> or
- /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
- /// (see <see cref="PhpMath.AbsolutizeRange"/> where <c>length</c> is infinity).
- /// </returns>
- [ImplementsFunction("strcspn")]
- public static int StrCSpn(string str, string acceptedChars, int offset)
- {
- return StrSpnInternal(str, acceptedChars, offset, int.MaxValue, true);
- }
-
- /// <summary>
- /// Finds a length of a segment consisting entirely of any characters except for specified ones.
- /// </summary>
- /// <param name="str">The string to be searched in.</param>
- /// <param name="acceptedChars">Accepted characters.</param>
- /// <param name="offset">The relativized offset of the first item of the slice.</param>
- /// <param name="length">The relativized length of the slice.</param>
- /// <returns>
- /// The length of the substring consisting entirely of characters not in <paramref name="acceptedChars"/> or
- /// zero if any argument is null. Search starts from absolutized <paramref name="offset"/>
- /// (see <see cref="PhpMath.AbsolutizeRange"/> and takes at most absolutized <paramref name="length"/> characters.
- /// </returns>
- [ImplementsFunction("strcspn")]
- public static int StrCSpn(string str, string acceptedChars, int offset, int length)
- {
- return StrSpnInternal(str, acceptedChars, offset, length, true);
- }
-
- /// <summary>
- /// Internal version of <see cref="StrSpn"/> (complement off) and <see cref="StrCSpn"/> (complement on).
- //…
Large files files are truncated, but you can click here to view the full file