|
40 | 40 | package org.jruby;
|
41 | 41 |
|
42 | 42 | import jnr.posix.POSIX;
|
| 43 | + |
| 44 | +import org.jcodings.Config; |
43 | 45 | import org.jcodings.Encoding;
|
| 46 | +import org.jcodings.IntHolder; |
44 | 47 | import org.jcodings.exception.EncodingException;
|
45 | 48 | import org.jcodings.specific.ASCIIEncoding;
|
46 | 49 | import org.jcodings.specific.USASCIIEncoding;
|
@@ -1632,8 +1635,8 @@ public IRubyObject casecmp_p(ThreadContext context, IRubyObject other) {
|
1632 | 1635 | Encoding enc = StringSupport.areCompatible(this, otherStr);
|
1633 | 1636 | if (enc == null) return context.nil;
|
1634 | 1637 |
|
1635 |
| - RubyString downcasedString = this.downcase(context); |
1636 |
| - RubyString otherDowncasedString = otherStr.downcase(context); |
| 1638 | + RubyString downcasedString = this.downcase(context, RubyObject.NULL_ARRAY); |
| 1639 | + RubyString otherDowncasedString = otherStr.downcase(context, RubyObject.NULL_ARRAY); |
1637 | 1640 | return downcasedString.equals(otherDowncasedString) ? context.runtime.getTrue() : context.runtime.getFalse();
|
1638 | 1641 | }
|
1639 | 1642 |
|
@@ -1703,60 +1706,6 @@ public IRubyObject match_p(ThreadContext context, IRubyObject pattern, IRubyObje
|
1703 | 1706 | return result;
|
1704 | 1707 | }
|
1705 | 1708 |
|
1706 |
| - /** rb_str_capitalize / rb_str_capitalize_bang |
1707 |
| - * |
1708 |
| - */ |
1709 |
| - public IRubyObject capitalize(ThreadContext context) { |
1710 |
| - return capitalize19(context); |
1711 |
| - } |
1712 |
| - |
1713 |
| - public IRubyObject capitalize_bang(ThreadContext context) { |
1714 |
| - return capitalize_bang19(context); |
1715 |
| - } |
1716 |
| - |
1717 |
| - @JRubyMethod(name = "capitalize") |
1718 |
| - public IRubyObject capitalize19(ThreadContext context) { |
1719 |
| - RubyString str = strDup(context.runtime); |
1720 |
| - str.capitalize_bang19(context); |
1721 |
| - return str; |
1722 |
| - } |
1723 |
| - |
1724 |
| - @JRubyMethod(name = "capitalize!") |
1725 |
| - public IRubyObject capitalize_bang19(ThreadContext context) { |
1726 |
| - Ruby runtime = context.runtime; |
1727 |
| - Encoding enc = checkDummyEncoding(); |
1728 |
| - |
1729 |
| - if (value.getRealSize() == 0) { |
1730 |
| - modifyCheck(); |
1731 |
| - return runtime.getNil(); |
1732 |
| - } |
1733 |
| - |
1734 |
| - modifyAndKeepCodeRange(); |
1735 |
| - |
1736 |
| - int s = value.getBegin(); |
1737 |
| - int end = s + value.getRealSize(); |
1738 |
| - byte[]bytes = value.getUnsafeBytes(); |
1739 |
| - boolean modify = false; |
1740 |
| - |
1741 |
| - int c = codePoint(runtime, enc, bytes, s, end); |
1742 |
| - if (enc.isLower(c)) { |
1743 |
| - enc.codeToMbc(toUpper(enc, c), bytes, s); |
1744 |
| - modify = true; |
1745 |
| - } |
1746 |
| - |
1747 |
| - s += codeLength(enc, c); |
1748 |
| - while (s < end) { |
1749 |
| - c = codePoint(runtime, enc, bytes, s, end); |
1750 |
| - if (enc.isUpper(c)) { |
1751 |
| - enc.codeToMbc(toLower(enc, c), bytes, s); |
1752 |
| - modify = true; |
1753 |
| - } |
1754 |
| - s += codeLength(enc, c); |
1755 |
| - } |
1756 |
| - |
1757 |
| - return modify ? this : runtime.getNil(); |
1758 |
| - } |
1759 |
| - |
1760 | 1709 | public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
|
1761 | 1710 | return op_ge19(context, other);
|
1762 | 1711 | }
|
@@ -1828,162 +1777,184 @@ public IRubyObject str_eql_p19(ThreadContext context, IRubyObject other) {
|
1828 | 1777 | */
|
1829 | 1778 | @Deprecated
|
1830 | 1779 | public RubyString upcase(ThreadContext context) {
|
1831 |
| - return upcase19(context); |
| 1780 | + return upcase19(context, RubyObject.NULL_ARRAY); |
1832 | 1781 | }
|
1833 | 1782 |
|
1834 | 1783 | @Deprecated
|
1835 | 1784 | public IRubyObject upcase_bang(ThreadContext context) {
|
1836 |
| - return upcase_bang19(context); |
| 1785 | + return upcase_bang19(context, RubyObject.NULL_ARRAY); |
1837 | 1786 | }
|
1838 | 1787 |
|
1839 |
| - @JRubyMethod(name = "upcase") |
1840 |
| - public RubyString upcase19(ThreadContext context) { |
| 1788 | + @JRubyMethod(name = "upcase", rest = true) |
| 1789 | + public RubyString upcase19(ThreadContext context, IRubyObject[] args) { |
1841 | 1790 | RubyString str = strDup(context.runtime);
|
1842 |
| - str.upcase_bang19(context); |
| 1791 | + str.upcase_bang19(context, args); |
1843 | 1792 | return str;
|
1844 | 1793 | }
|
1845 | 1794 |
|
1846 |
| - @JRubyMethod(name = "upcase!") |
1847 |
| - public IRubyObject upcase_bang19(ThreadContext context) { |
| 1795 | + @JRubyMethod(name = "upcase!", rest = true) |
| 1796 | + public IRubyObject upcase_bang19(ThreadContext context, IRubyObject[] args) { |
1848 | 1797 | Ruby runtime = context.runtime;
|
1849 |
| - Encoding enc = checkDummyEncoding(); |
1850 |
| - |
1851 |
| - if (value.getRealSize() == 0) { |
1852 |
| - modifyCheck(); |
1853 |
| - return runtime.getNil(); |
1854 |
| - } |
1855 |
| - |
| 1798 | + int flags = StringSupport.checkCaseOptions(runtime, args, Config.CASE_UPCASE); |
1856 | 1799 | modifyAndKeepCodeRange();
|
| 1800 | + Encoding enc = checkDummyEncoding(); |
1857 | 1801 |
|
1858 |
| - int s = value.getBegin(); |
1859 |
| - int end = s + value.getRealSize(); |
1860 |
| - byte[]bytes = value.getUnsafeBytes(); |
1861 |
| - |
1862 |
| - if (singleByteOptimizable(enc)) { |
1863 |
| - return singleByteUpcase(runtime, bytes, s, end); |
| 1802 | + if (((flags & Config.CASE_ASCII_ONLY) != 0 && (enc.isUTF8() || enc.maxLength() == 1)) || |
| 1803 | + (flags & Config.CASE_FOLD_TURKISH_AZERI) == 0 && getCodeRange() == CR_7BIT) { |
| 1804 | + int s = value.getBegin(); |
| 1805 | + int end = s + value.getRealSize(); |
| 1806 | + byte[]bytes = value.getUnsafeBytes(); |
| 1807 | + while (s < end) { |
| 1808 | + int c = bytes[s] & 0xff; |
| 1809 | + if (Encoding.isAscii(c) && 'a' <= c && c <= 'z') { |
| 1810 | + bytes[s] = (byte)('A' + (c - 'a')); |
| 1811 | + flags |= Config.CASE_MODIFIED; |
| 1812 | + } |
| 1813 | + s++; |
| 1814 | + } |
| 1815 | + } else if ((flags & Config.CASE_ASCII_ONLY) != 0) { |
| 1816 | + flags = StringSupport.asciiOnlyCaseMap(runtime, this, flags, enc); |
1864 | 1817 | } else {
|
1865 |
| - return multiByteUpcase(runtime, enc, bytes, s, end); |
| 1818 | + IntHolder flagsP = new IntHolder(); |
| 1819 | + flagsP.value = flags; |
| 1820 | + value = StringSupport.caseMap(runtime, value, flagsP); |
| 1821 | + flags = flagsP.value; |
1866 | 1822 | }
|
1867 |
| - } |
1868 |
| - |
1869 |
| - private IRubyObject singleByteUpcase(Ruby runtime, byte[]bytes, int s, int end) { |
1870 |
| - boolean modify = StringSupport.singleByteUpcase(bytes, s, end); |
1871 |
| - |
1872 |
| - return modify ? this : runtime.getNil(); |
1873 |
| - } |
1874 | 1823 |
|
1875 |
| - private IRubyObject multiByteUpcase(Ruby runtime, Encoding enc, byte[]bytes, int s, int end) { |
1876 |
| - try { |
1877 |
| - boolean modify = StringSupport.multiByteUpcase(enc, bytes, s, end); |
1878 |
| - |
1879 |
| - return modify ? this : runtime.getNil(); |
1880 |
| - } catch (IllegalArgumentException e) { |
1881 |
| - throw runtime.newArgumentError(e.getMessage()); |
1882 |
| - } |
| 1824 | + return ((flags & Config.CASE_MODIFIED) != 0) ? this : context.nil; |
1883 | 1825 | }
|
1884 | 1826 |
|
1885 | 1827 | @Deprecated
|
1886 | 1828 | public RubyString downcase19(ThreadContext context) {
|
1887 |
| - return downcase(context); |
| 1829 | + return downcase(context, RubyObject.NULL_ARRAY); |
1888 | 1830 | }
|
1889 | 1831 |
|
1890 | 1832 | @Deprecated
|
1891 | 1833 | public IRubyObject downcase_bang19(ThreadContext context) {
|
1892 |
| - return downcase_bang(context); |
| 1834 | + return downcase_bang(context, RubyObject.NULL_ARRAY); |
1893 | 1835 | }
|
1894 | 1836 |
|
1895 | 1837 | /** rb_str_downcase / rb_str_downcase_bang
|
1896 | 1838 | *
|
1897 | 1839 | */
|
1898 | 1840 |
|
1899 |
| - @JRubyMethod(name = "downcase") |
1900 |
| - public RubyString downcase(ThreadContext context) { |
| 1841 | + @JRubyMethod(name = "downcase", rest = true) |
| 1842 | + public RubyString downcase(ThreadContext context, IRubyObject[] args) { |
1901 | 1843 | RubyString str = strDup(context.runtime);
|
1902 |
| - str.downcase_bang(context); |
| 1844 | + str.downcase_bang(context, args); |
1903 | 1845 | return str;
|
1904 | 1846 | }
|
1905 | 1847 |
|
1906 |
| - @JRubyMethod(name = "downcase!") |
1907 |
| - public IRubyObject downcase_bang(ThreadContext context) { |
| 1848 | + @JRubyMethod(name = "downcase!", rest = true) |
| 1849 | + public IRubyObject downcase_bang(ThreadContext context, IRubyObject[] args) { |
| 1850 | + Ruby runtime = context.runtime; |
| 1851 | + int flags = StringSupport.checkCaseOptions(runtime, args, Config.CASE_DOWNCASE); |
| 1852 | + modifyAndKeepCodeRange(); |
1908 | 1853 | Encoding enc = checkDummyEncoding();
|
1909 | 1854 |
|
1910 |
| - if (value.getRealSize() == 0) { |
1911 |
| - modifyCheck(); |
1912 |
| - return context.nil; |
| 1855 | + if (((flags & Config.CASE_ASCII_ONLY) != 0 && (enc.isUTF8() || enc.maxLength() == 1)) || |
| 1856 | + (flags & Config.CASE_FOLD_TURKISH_AZERI) == 0 && getCodeRange() == CR_7BIT) { |
| 1857 | + int s = value.getBegin(); |
| 1858 | + int end = s + value.getRealSize(); |
| 1859 | + byte[]bytes = value.getUnsafeBytes(); |
| 1860 | + while (s < end) { |
| 1861 | + int c = bytes[s] & 0xff; |
| 1862 | + if (Encoding.isAscii(c) && 'A' <= c && c <= 'Z') { |
| 1863 | + bytes[s] = (byte)('a' + (c - 'A')); |
| 1864 | + flags |= Config.CASE_MODIFIED; |
| 1865 | + } |
| 1866 | + s++; |
| 1867 | + } |
| 1868 | + } else if ((flags & Config.CASE_ASCII_ONLY) != 0) { |
| 1869 | + flags = StringSupport.asciiOnlyCaseMap(runtime, this, flags, enc); |
| 1870 | + } else { |
| 1871 | + IntHolder flagsP = new IntHolder(); |
| 1872 | + flagsP.value = flags; |
| 1873 | + value = StringSupport.caseMap(runtime, value, flagsP); |
| 1874 | + flags = flagsP.value; |
1913 | 1875 | }
|
1914 | 1876 |
|
1915 |
| - modifyAndKeepCodeRange(); |
1916 |
| - |
1917 |
| - int s = value.getBegin(); |
1918 |
| - int end = s + value.getRealSize(); |
1919 |
| - byte[] bytes = value.getUnsafeBytes(); |
| 1877 | + return ((flags & Config.CASE_MODIFIED) != 0) ? this : context.nil; |
| 1878 | + } |
1920 | 1879 |
|
1921 |
| - if (singleByteOptimizable(enc)) { |
1922 |
| - return singleByteDowncase(context, bytes, s, end); |
1923 |
| - } |
1924 |
| - return multiByteDowncase(context, enc, bytes, s, end); |
| 1880 | + /** rb_str_swapcase / rb_str_swapcase_bang |
| 1881 | + * |
| 1882 | + */ |
| 1883 | + public RubyString swapcase(ThreadContext context) { |
| 1884 | + return swapcase19(context, RubyObject.NULL_ARRAY); |
1925 | 1885 | }
|
1926 | 1886 |
|
1927 |
| - private IRubyObject singleByteDowncase(ThreadContext context, byte[] bytes, int s, int end) { |
1928 |
| - boolean modify = StringSupport.singleByteDowncase(bytes, s, end); |
| 1887 | + public IRubyObject swapcase_bang(ThreadContext context) { |
| 1888 | + return swapcase_bang19(context, RubyObject.NULL_ARRAY); |
| 1889 | + } |
1929 | 1890 |
|
1930 |
| - return modify ? this : context.nil; |
| 1891 | + @JRubyMethod(name = "swapcase", rest = true) |
| 1892 | + public RubyString swapcase19(ThreadContext context, IRubyObject[] args) { |
| 1893 | + RubyString str = strDup(context.runtime); |
| 1894 | + str.swapcase_bang19(context, args); |
| 1895 | + return str; |
1931 | 1896 | }
|
1932 | 1897 |
|
1933 |
| - private IRubyObject multiByteDowncase(ThreadContext context, Encoding enc, byte[] bytes, int s, int end) { |
1934 |
| - try { |
1935 |
| - boolean modify = StringSupport.multiByteDowncase(enc, bytes, s, end); |
| 1898 | + @JRubyMethod(name = "swapcase!", rest = true) |
| 1899 | + public IRubyObject swapcase_bang19(ThreadContext context, IRubyObject[] args) { |
| 1900 | + Ruby runtime = context.runtime; |
| 1901 | + int flags = StringSupport.checkCaseOptions(runtime, args, Config.CASE_UPCASE | Config.CASE_DOWNCASE); |
| 1902 | + modifyAndKeepCodeRange(); |
| 1903 | + Encoding enc = checkDummyEncoding(); |
1936 | 1904 |
|
1937 |
| - return modify ? this : context.nil; |
1938 |
| - } catch (IllegalArgumentException e) { |
1939 |
| - throw context.runtime.newArgumentError(e.getMessage()); |
| 1905 | + if ((flags & Config.CASE_ASCII_ONLY) != 0) { |
| 1906 | + StringSupport.asciiOnlyCaseMap(runtime, this, flags, enc); |
| 1907 | + } else { |
| 1908 | + IntHolder flagsP = new IntHolder(); |
| 1909 | + flagsP.value = flags; |
| 1910 | + value = StringSupport.caseMap(runtime, value, flagsP); |
| 1911 | + flags = flagsP.value; |
1940 | 1912 | }
|
1941 |
| - } |
1942 | 1913 |
|
| 1914 | + return ((flags & Config.CASE_MODIFIED) != 0) ? this : context.nil; |
| 1915 | + } |
1943 | 1916 |
|
1944 |
| - /** rb_str_swapcase / rb_str_swapcase_bang |
1945 |
| - * |
1946 |
| - */ |
1947 |
| - public RubyString swapcase(ThreadContext context) { |
1948 |
| - return swapcase19(context); |
| 1917 | + /** rb_str_capitalize / rb_str_capitalize_bang |
| 1918 | + * |
| 1919 | + */ |
| 1920 | + public IRubyObject capitalize(ThreadContext context) { |
| 1921 | + return capitalize19(context, RubyObject.NULL_ARRAY); |
1949 | 1922 | }
|
1950 | 1923 |
|
1951 |
| - public IRubyObject swapcase_bang(ThreadContext context) { |
1952 |
| - return swapcase_bang19(context); |
| 1924 | + public IRubyObject capitalize_bang(ThreadContext context) { |
| 1925 | + return capitalize_bang19(context, RubyObject.NULL_ARRAY); |
1953 | 1926 | }
|
1954 | 1927 |
|
1955 |
| - @JRubyMethod(name = "swapcase") |
1956 |
| - public RubyString swapcase19(ThreadContext context) { |
| 1928 | + @JRubyMethod(name = "capitalize", rest = true) |
| 1929 | + public IRubyObject capitalize19(ThreadContext context, IRubyObject[] args) { |
1957 | 1930 | RubyString str = strDup(context.runtime);
|
1958 |
| - str.swapcase_bang19(context); |
| 1931 | + str.capitalize_bang19(context, args); |
1959 | 1932 | return str;
|
1960 | 1933 | }
|
1961 | 1934 |
|
1962 |
| - @JRubyMethod(name = "swapcase!") |
1963 |
| - public IRubyObject swapcase_bang19(ThreadContext context) { |
| 1935 | + @JRubyMethod(name = "capitalize!", rest = true) |
| 1936 | + public IRubyObject capitalize_bang19(ThreadContext context, IRubyObject[] args) { |
1964 | 1937 | Ruby runtime = context.runtime;
|
| 1938 | + int flags = StringSupport.checkCaseOptions(runtime, args, Config.CASE_UPCASE | Config.CASE_TITLECASE); |
1965 | 1939 | Encoding enc = checkDummyEncoding();
|
| 1940 | + |
1966 | 1941 | if (value.getRealSize() == 0) {
|
1967 | 1942 | modifyCheck();
|
1968 | 1943 | return runtime.getNil();
|
1969 | 1944 | }
|
1970 |
| - modifyAndKeepCodeRange(); |
1971 | 1945 |
|
1972 |
| - int s = value.getBegin(); |
1973 |
| - int end = s + value.getRealSize(); |
1974 |
| - byte[]bytes = value.getUnsafeBytes(); |
| 1946 | + modifyAndKeepCodeRange(); |
1975 | 1947 |
|
1976 |
| - if (singleByteOptimizable(enc)) { |
1977 |
| - if (StringSupport.singleByteSwapcase(bytes, s, end)) { |
1978 |
| - return this; |
1979 |
| - } |
| 1948 | + if ((flags & Config.CASE_ASCII_ONLY) != 0) { |
| 1949 | + StringSupport.asciiOnlyCaseMap(runtime, this, flags, enc); |
1980 | 1950 | } else {
|
1981 |
| - if (StringSupport.multiByteSwapcase(runtime, enc, bytes, s, end)) { |
1982 |
| - return this; |
1983 |
| - } |
| 1951 | + IntHolder flagsP = new IntHolder(); |
| 1952 | + flagsP.value = flags; |
| 1953 | + value = StringSupport.caseMap(runtime, value, flagsP); |
| 1954 | + flags = flagsP.value; |
1984 | 1955 | }
|
1985 | 1956 |
|
1986 |
| - return runtime.getNil(); |
| 1957 | + return ((flags & Config.CASE_MODIFIED) != 0) ? this : context.nil; |
1987 | 1958 | }
|
1988 | 1959 |
|
1989 | 1960 | /** rb_str_dump
|
|