Merge lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment into lp:akiban-persistit

Proposed by Peter Beaman
Status: Merged
Approved by: Nathan Williams
Approved revision: 427
Merged at revision: 425
Proposed branch: lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment
Merge into: lp:akiban-persistit
Diff against target: 976 lines (+382/-265)
7 files modified
src/main/java/com/persistit/Key.java (+286/-194)
src/main/java/com/persistit/Persistit.java (+1/-1)
src/main/java/com/persistit/Transaction.java (+1/-1)
src/main/java/com/persistit/exception/KeyTooLongException.java (+39/-0)
src/test/java/com/persistit/TransactionAbandonedTest.java (+16/-14)
src/test/java/com/persistit/unit/ExchangeTest.java (+3/-3)
src/test/java/com/persistit/unit/KeyTest1.java (+36/-52)
To merge this branch: bzr merge lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment
Reviewer Review Type Date Requested Status
Nathan Williams Approve
Akiban Build User Needs Fixing
Review via email: mp+154422@code.launchpad.net

Description of the change

Add a test to exercise various places where code in the Key#append methods attempts to write past the end of the encoded byte array.

Add try/catch blocks to all relevant Key#append methods to catch AIOOBE and instead throw newly added KeyTooLongException. KeyTooLongException is unchecked so as not to change lots of use cases where Keys modifications occur.

Strategy is to catch the AIOOBE, which is a rare, unexpected event rather than to add bound-checking code everywhere. Since the array operations already do bounds checking this seems like a better approach.

To post a comment you must log in.
Revision history for this message
Nathan Williams (nwilliams) wrote :

We should always restore _size in the even of an error, right? There are a handful of places that don't do that, such as append(boolean). Perhaps adding the size to restore to the new tooLong() helper method would enforce that nicely.

Otherwise seems simple enough.

review: Needs Information
Revision history for this message
Peter Beaman (pbeaman) wrote :

I took some care to verify that the places that don't restore the value of
_size also don't change it.

On Wed, Mar 20, 2013 at 2:03 PM, Nathan Williams <email address hidden>wrote:

> Review: Needs Information
>
> We should always restore _size in the even of an error, right? There are a
> handful of places that don't do that, such as append(boolean). Perhaps
> adding the size to restore to the new tooLong() helper method would enforce
> that nicely.
>
> Otherwise seems simple enough.
> --
>
> https://code.launchpad.net/~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment/+merge/154422
> You are the owner of
> lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment.
>

Revision history for this message
Peter Beaman (pbeaman) wrote :

Persuaded. Modified all Key#append methods to save starting size and restore it in the tooLong method. As you mentioned, this pattern will help guard against newly added errors.

Revision history for this message
Nathan Williams (nwilliams) wrote :

Thanks Peter!

review: Approve
Revision history for this message
Peter Beaman (pbeaman) wrote :

Found some new problems and pushed back to Needs Review. Basically, the new KeyTest1#testKeyTooLong does not loop correctly and does not actually test all object values in the TEST_OBJECTS array. In particular, the method Key#appendString did not handle AIOOBE, and that's the one we are more concerned about.

Revision history for this message
Akiban Build User (build-akiban) wrote :

There were 2 failures during build/test:

* job server-build failed at build number 3912: http://172.16.20.104:8080/job/server-build/3912/

* view must-pass failed: server-build is aborted

review: Needs Fixing
427. By Peter Beaman

Fix testKeyTooLong. Modify appendString to throw KeyTooLongException rather than ConversionException.

Revision history for this message
Peter Beaman (pbeaman) wrote :

New modifications:

correct KeyTest1#testKeyTooLongException

add try/catch to appendString, appendNull, appendBefore and appendAfter

Revision history for this message
Nathan Williams (nwilliams) wrote :

Looks good, again.

review: Approve
Revision history for this message
Peter Beaman (pbeaman) wrote :

Note on a concern I researched a bit. We mangle the keys in the sort tree. If the server were to use those keys to deliver results from a sort or SELECT DISTINCT (like a covering index) then of course we would return the wrong result. However, I believe this does not happen.

It appears the Sort_Tree operator stores and reads data back from value instances stored in the tree, rather than trying to use the key as a source. Specifically, in PValueSorterAdapter$InternalPAdapter, there is a method putToHolders that copies data from a valueSource to the result row. The valueSource is a PersistitValuePValueSource, not a PersistitKeyPValueSource.

The code path is complex, so I would appreciate confirmation that this is the case. But from my reading I can only see a path that reads from a PersistitValuePValueSource.

Revision history for this message
Mike McMahon (mmcm) wrote :

> The code path is complex, so I would appreciate confirmation that this is the case. But from my reading I can only see a path that reads from a PersistitValuePValueSource.

Yes, we are good.

Look at Sorter.loadTree() and its calls to createKey() and createValue(). The entire incoming Row is encoded in the value and that is always the output source. This allows it to support cases where you sort on a subset of the columns that pass through. No particular attempt is made to suppress duplication (as you say, like a covering index).

Revision history for this message
Peter Beaman (pbeaman) wrote :

The change I made this morning in loadTree (to close the cursor when
there's an Exception) is wrong. Will correct it shortly. I am working on
additional tests.

On Thu, Mar 21, 2013 at 1:24 PM, Mike McMahon <email address hidden> wrote:

> > The code path is complex, so I would appreciate confirmation that this
> is the case. But from my reading I can only see a path that reads from a
> PersistitValuePValueSource.
>
> Yes, we are good.
>
> Look at Sorter.loadTree() and its calls to createKey() and createValue().
> The entire incoming Row is encoded in the value and that is always the
> output source. This allows it to support cases where you sort on a subset
> of the columns that pass through. No particular attempt is made to suppress
> duplication (as you say, like a covering index).
> --
>
> https://code.launchpad.net/~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment/+merge/154422
> You are the owner of
> lp:~pbeaman/akiban-persistit/fix-1157809-aioobe-on-append-key-segment.
>

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/main/java/com/persistit/Key.java'
--- src/main/java/com/persistit/Key.java 2013-01-19 17:42:22 +0000
+++ src/main/java/com/persistit/Key.java 2013-03-20 19:33:22 +0000
@@ -27,6 +27,7 @@
27import com.persistit.encoding.KeyRenderer;27import com.persistit.encoding.KeyRenderer;
28import com.persistit.exception.ConversionException;28import com.persistit.exception.ConversionException;
29import com.persistit.exception.InvalidKeyException;29import com.persistit.exception.InvalidKeyException;
30import com.persistit.exception.KeyTooLongException;
30import com.persistit.exception.MissingKeySegmentException;31import com.persistit.exception.MissingKeySegmentException;
31import com.persistit.util.Util;32import com.persistit.util.Util;
3233
@@ -924,8 +925,6 @@
924 * Key copiedKey = new Key(originalKey);925 * Key copiedKey = new Key(originalKey);
925 * </pre></code>926 * </pre></code>
926 * 927 *
927 *
928 *
929 * @param key928 * @param key
930 * The <code>Key</code> to copy.929 * The <code>Key</code> to copy.
931 */930 */
@@ -947,17 +946,25 @@
947 key.bumpGeneration();946 key.bumpGeneration();
948 }947 }
949948
949 /**
950 * Construct a <code>Key</code> with a maximum length of
951 * {@value #MAX_KEY_LENGTH}.
952 *
953 * @param persistit
954 */
950 public Key(final Persistit persistit) {955 public Key(final Persistit persistit) {
951 this(persistit, MAX_KEY_LENGTH);956 this(persistit, MAX_KEY_LENGTH);
952 }957 }
953958
954 /**959 /**
955 * Constructs a <code>Key</code> with the specified maximum length.960 * Construct a <code>Key</code> with the specified maximum length.
956 * 961 *
962 * @param Persistit
963 * the Persistit instance
957 * @param maxLength964 * @param maxLength
958 * The maximum length965 * The maximum length
959 */966 */
960 private Key(final Persistit persistit, final int maxLength) {967 public Key(final Persistit persistit, final int maxLength) {
961 _persistit = persistit;968 _persistit = persistit;
962 if (maxLength <= 0) {969 if (maxLength <= 0) {
963 throw new IllegalArgumentException("Key length must be positive");970 throw new IllegalArgumentException("Key length must be positive");
@@ -1479,13 +1486,11 @@
1479 } else if (z0 != 0 && z1 == (byte) 1) {1486 } else if (z0 != 0 && z1 == (byte) 1) {
1480 nudged = Nudged.RIGHT;1487 nudged = Nudged.RIGHT;
1481 _bytes[_size - 1] = 0;1488 _bytes[_size - 1] = 0;
1482 } else if (z0 != 0 && z1 != 0) {1489 } else if (z0 != 0 && z1 != 0 && _size < _maxSize) {
1483 nudged = Nudged.LEFT;1490 nudged = Nudged.LEFT;
1484 save = _bytes[_size];1491 save = _bytes[_size];
1485 _bytes[_size] = (byte) 0;1492 _bytes[_size] = (byte) 0;
1486 _size++;1493 _size++;
1487 // _bytes[_size - 2]++;
1488 // _bytes[_size - 1] = (byte) 0;
1489 }1494 }
1490 }1495 }
14911496
@@ -1539,11 +1544,15 @@
1539 * @return This <code>Key</code>, to permit method call chaining1544 * @return This <code>Key</code>, to permit method call chaining
1540 */1545 */
1541 public Key append(final boolean v) {1546 public Key append(final boolean v) {
1542 testValidForAppend();1547 final int save = _size;
1543 int size = _size;1548 try {
1544 _bytes[size++] = v ? (byte) TYPE_BOOLEAN_TRUE : (byte) TYPE_BOOLEAN_FALSE;1549 testValidForAppend();
15451550 int size = _size;
1546 return endSegment(size);1551 _bytes[size++] = v ? (byte) TYPE_BOOLEAN_TRUE : (byte) TYPE_BOOLEAN_FALSE;
1552 return endSegment(size);
1553 } catch (final ArrayIndexOutOfBoundsException e) {
1554 return tooLong(save);
1555 }
1547 }1556 }
15481557
1549 /*1558 /*
@@ -1629,21 +1638,26 @@
1629 * @return This <code>Key</code>, to permit method call chaining1638 * @return This <code>Key</code>, to permit method call chaining
1630 */1639 */
1631 public Key append(final byte v) {1640 public Key append(final byte v) {
1632 testValidForAppend();1641 final int save = _size;
1633 int size = _size;1642 try {
1634 if (v > 0) {1643 testValidForAppend();
1635 _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE + 1;1644 int size = _size;
1636 _bytes[size++] = (byte) (0x80 | v);1645 if (v > 0) {
1637 } else if (v < 0) {1646 _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE + 1;
1638 _bytes[size++] = TYPE_BYTE;1647 _bytes[size++] = (byte) (0x80 | v);
1639 _bytes[size++] = (byte) (0x80 | v);1648 } else if (v < 0) {
1640 } else // v == 01649 _bytes[size++] = TYPE_BYTE;
1641 {1650 _bytes[size++] = (byte) (0x80 | v);
1642 _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE;1651 } else // v == 0
1652 {
1653 _bytes[size++] = TYPE_BYTE + EWIDTH_BYTE;
1654 }
1655 // Close out the segment.
1656
1657 return endSegment(size);
1658 } catch (final ArrayIndexOutOfBoundsException e) {
1659 return tooLong(save);
1643 }1660 }
1644 // Close out the segment.
1645
1646 return endSegment(size);
1647 }1661 }
16481662
1649 /**1663 /**
@@ -1654,9 +1668,65 @@
1654 * @return This <code>Key</code>, to permit method call chaining1668 * @return This <code>Key</code>, to permit method call chaining
1655 */1669 */
1656 public Key append(final short v) {1670 public Key append(final short v) {
1657 testValidForAppend();1671 final int save = _size;
1658 int size = _size;1672 try {
1659 if (v >= 0) {1673 testValidForAppend();
1674 int size = _size;
1675 if (v >= 0) {
1676 int scale = 3;
1677 if (v > 0x3FFF)
1678 scale = 0;
1679 else if (v > 0x007F)
1680 scale = 1;
1681 else if (v > 0x0000)
1682 scale = 2;
1683 _bytes[size++] = (byte) (TYPE_SHORT + EWIDTH_SHORT * 2 - scale);
1684 switch (scale) {
1685 // control falls through intentionally
1686 case 0:
1687 _bytes[size++] = (byte) (0x80 | (v >>> 14));
1688 case 1:
1689 _bytes[size++] = (byte) (0x80 | (v >>> 7));
1690 case 2:
1691 _bytes[size++] = (byte) (0x80 | v);
1692 }
1693 } else {
1694 int scale = 2;
1695 if (v < -0x3FFF)
1696 scale = 0;
1697 else if (v < -0x007F)
1698 scale = 1;
1699 _bytes[size++] = (byte) (TYPE_SHORT + scale);
1700 switch (scale) {
1701 // control falls through intentionally
1702 case 0:
1703 _bytes[size++] = (byte) (0x80 | (v >>> 14));
1704 case 1:
1705 _bytes[size++] = (byte) (0x80 | (v >>> 7));
1706 case 2:
1707 _bytes[size++] = (byte) (0x80 | v);
1708 }
1709 }
1710 // Close out the segment.
1711
1712 return endSegment(size);
1713 } catch (final ArrayIndexOutOfBoundsException e) {
1714 return tooLong(save);
1715 }
1716 }
1717
1718 /**
1719 * Encodes and appends a char value to the key.
1720 *
1721 * @param v
1722 * The char value to append
1723 * @return This <code>Key</code>, to permit method call chaining
1724 */
1725 public Key append(final char v) {
1726 final int save = _size;
1727 try {
1728 testValidForAppend();
1729 int size = _size;
1660 int scale = 3;1730 int scale = 3;
1661 if (v > 0x3FFF)1731 if (v > 0x3FFF)
1662 scale = 0;1732 scale = 0;
@@ -1664,67 +1734,21 @@
1664 scale = 1;1734 scale = 1;
1665 else if (v > 0x0000)1735 else if (v > 0x0000)
1666 scale = 2;1736 scale = 2;
1667 _bytes[size++] = (byte) (TYPE_SHORT + EWIDTH_SHORT * 2 - scale);1737 _bytes[size++] = (byte) (TYPE_CHAR + EWIDTH_CHAR - scale);
1668 switch (scale) {1738 switch (scale) {
1669 // control falls through intentionally1739 // control falls through intentionally
1670 case 0:1740 case 0:
1671 _bytes[size++] = (byte) (0x80 | (v >>> 14));1741 _bytes[size++] = (byte) (0x80 | (v >>> 14));
1672 case 1:1742 case 1:
1673 _bytes[size++] = (byte) (0x80 | (v >>> 7));1743 _bytes[size++] = (byte) (0x80 | (v >>> 7));
1674 case 2:1744 case 2:
1675 _bytes[size++] = (byte) (0x80 | v);1745 _bytes[size++] = (byte) (0x80 | v);
1676 }1746 }
1677 } else {1747
1678 int scale = 2;1748 return endSegment(size);
1679 if (v < -0x3FFF)1749 } catch (final ArrayIndexOutOfBoundsException e) {
1680 scale = 0;1750 return tooLong(save);
1681 else if (v < -0x007F)1751 }
1682 scale = 1;
1683 _bytes[size++] = (byte) (TYPE_SHORT + scale);
1684 switch (scale) {
1685 // control falls through intentionally
1686 case 0:
1687 _bytes[size++] = (byte) (0x80 | (v >>> 14));
1688 case 1:
1689 _bytes[size++] = (byte) (0x80 | (v >>> 7));
1690 case 2:
1691 _bytes[size++] = (byte) (0x80 | v);
1692 }
1693 }
1694 // Close out the segment.
1695
1696 return endSegment(size);
1697 }
1698
1699 /**
1700 * Encodes and appends a char value to the key.
1701 *
1702 * @param v
1703 * The char value to append
1704 * @return This <code>Key</code>, to permit method call chaining
1705 */
1706 public Key append(final char v) {
1707 testValidForAppend();
1708 int size = _size;
1709 int scale = 3;
1710 if (v > 0x3FFF)
1711 scale = 0;
1712 else if (v > 0x007F)
1713 scale = 1;
1714 else if (v > 0x0000)
1715 scale = 2;
1716 _bytes[size++] = (byte) (TYPE_CHAR + EWIDTH_CHAR - scale);
1717 switch (scale) {
1718 // control falls through intentionally
1719 case 0:
1720 _bytes[size++] = (byte) (0x80 | (v >>> 14));
1721 case 1:
1722 _bytes[size++] = (byte) (0x80 | (v >>> 7));
1723 case 2:
1724 _bytes[size++] = (byte) (0x80 | v);
1725 }
1726
1727 return endSegment(size);
1728 }1752 }
17291753
1730 /**1754 /**
@@ -1735,11 +1759,14 @@
1735 * @return This <code>Key</code>, to permit method call chaining1759 * @return This <code>Key</code>, to permit method call chaining
1736 */1760 */
1737 public Key append(final int v) {1761 public Key append(final int v) {
1738 testValidForAppend();1762 final int save = _size;
1739 final int size = appendIntInternal(v);1763 try {
1740 // Close out the segment.1764 testValidForAppend();
17411765 final int size = appendIntInternal(v);
1742 return endSegment(size);1766 return endSegment(size);
1767 } catch (final ArrayIndexOutOfBoundsException e) {
1768 return tooLong(save);
1769 }
1743 }1770 }
17441771
1745 /**1772 /**
@@ -1821,9 +1848,14 @@
1821 * @return This <code>Key</code>, to permit method call chaining1848 * @return This <code>Key</code>, to permit method call chaining
1822 */1849 */
1823 public Key append(final long v) {1850 public Key append(final long v) {
1824 testValidForAppend();1851 final int save = _size;
1825 final int size = appendLongInternal(v);1852 try {
1826 return endSegment(size);1853 testValidForAppend();
1854 final int size = appendLongInternal(v);
1855 return endSegment(size);
1856 } catch (final ArrayIndexOutOfBoundsException e) {
1857 return tooLong(save);
1858 }
1827 }1859 }
18281860
1829 private int appendLongInternal(final long v) {1861 private int appendLongInternal(final long v) {
@@ -1921,21 +1953,26 @@
1921 * @return This <code>Key</code>, to permit method call chaining1953 * @return This <code>Key</code>, to permit method call chaining
1922 */1954 */
1923 public Key append(final float v) {1955 public Key append(final float v) {
1924 testValidForAppend();1956 final int save = _size;
1925 int bits = Float.floatToIntBits(v);1957 try {
1926 int size = _size;1958 testValidForAppend();
1927 _bytes[size++] = TYPE_FLOAT;1959 int bits = Float.floatToIntBits(v);
1928 if (bits < 0) {1960 int size = _size;
1929 bits = ~bits;1961 _bytes[size++] = TYPE_FLOAT;
1930 } else {1962 if (bits < 0) {
1931 bits ^= 0x80000000;1963 bits = ~bits;
1932 }1964 } else {
1933 while (bits != 0) {1965 bits ^= 0x80000000;
1934 _bytes[size++] = (byte) (0x80 | (bits >> 25));1966 }
1935 bits <<= 7;1967 while (bits != 0) {
1936 }1968 _bytes[size++] = (byte) (0x80 | (bits >> 25));
1969 bits <<= 7;
1970 }
19371971
1938 return endSegment(size);1972 return endSegment(size);
1973 } catch (final ArrayIndexOutOfBoundsException e) {
1974 return tooLong(save);
1975 }
1939 }1976 }
19401977
1941 /**1978 /**
@@ -1946,21 +1983,26 @@
1946 * @return This <code>Key</code>, to permit method call chaining1983 * @return This <code>Key</code>, to permit method call chaining
1947 */1984 */
1948 public Key append(final double v) {1985 public Key append(final double v) {
1949 testValidForAppend();1986 final int save = _size;
1950 long bits = Double.doubleToLongBits(v);1987 try {
1951 int size = _size;1988 testValidForAppend();
1952 _bytes[size++] = TYPE_DOUBLE;1989 long bits = Double.doubleToLongBits(v);
1953 if (bits < 0) {1990 int size = _size;
1954 bits = ~bits;1991 _bytes[size++] = TYPE_DOUBLE;
1955 } else {1992 if (bits < 0) {
1956 bits ^= 0x8000000000000000L;1993 bits = ~bits;
1957 }1994 } else {
1958 while (bits != 0) {1995 bits ^= 0x8000000000000000L;
1959 _bytes[size++] = (byte) (0x80 | (bits >> 57));1996 }
1960 bits <<= 7;1997 while (bits != 0) {
1961 }1998 _bytes[size++] = (byte) (0x80 | (bits >> 57));
1999 bits <<= 7;
2000 }
19622001
1963 return endSegment(size);2002 return endSegment(size);
2003 } catch (final ArrayIndexOutOfBoundsException e) {
2004 return tooLong(save);
2005 }
1964 }2006 }
19652007
1966 /**2008 /**
@@ -2072,6 +2114,7 @@
2072 }2114 }
20732115
2074 throw new ConversionException("Object class " + object.getClass().getName() + " can't be used in a Key");2116 throw new ConversionException("Object class " + object.getClass().getName() + " can't be used in a Key");
2117
2075 }2118 }
20762119
2077 /**2120 /**
@@ -2081,16 +2124,22 @@
2081 * 2124 *
2082 * @param key2125 * @param key
2083 */2126 */
2084 public void appendKeySegment(final Key key) {2127 public Key appendKeySegment(final Key key) {
2085 int length = 0;2128 final int save = _size;
2086 for (int index = key.getIndex(); index < key.getEncodedSize(); index++) {2129 try {
2087 length++;2130 int length = 0;
2088 if (key.getEncodedBytes()[index] == 0) {2131 for (int index = key.getIndex(); index < key.getEncodedSize(); index++) {
2089 break;2132 length++;
2133 if (key.getEncodedBytes()[index] == 0) {
2134 length--;
2135 break;
2136 }
2090 }2137 }
2138 System.arraycopy(key.getEncodedBytes(), key.getIndex(), _bytes, _size, length);
2139 return endSegment(_size + length);
2140 } catch (final ArrayIndexOutOfBoundsException e) {
2141 return tooLong(save);
2091 }2142 }
2092 System.arraycopy(key.getEncodedBytes(), key.getIndex(), _bytes, _size, length);
2093 _size += length;
2094 }2143 }
20952144
2096 /**2145 /**
@@ -3325,33 +3374,34 @@
3325 * @return This <code>Key</code>, to permit method call chaining3374 * @return This <code>Key</code>, to permit method call chaining
3326 */3375 */
3327 private Key appendString(final CharSequence s, final CoderContext context) {3376 private Key appendString(final CharSequence s, final CoderContext context) {
3328 notLeftOrRightGuard();3377 final int save = _size;
3329 testValidForAppend();3378 try {
3330 final int strlen = s.length();3379 notLeftOrRightGuard();
3331 if (strlen > _maxSize) {3380 testValidForAppend();
3332 throw new ConversionException("Requested size=" + strlen + " exceeds maximum size=" + _maxSize);3381 final int strlen = s.length();
3333 }3382 int size = _size;
3334 int size = _size;3383 _bytes[size++] = (byte) TYPE_STRING;
3335 _bytes[size++] = (byte) TYPE_STRING;
33363384
3337 for (int i = 0; i < strlen; i++) {3385 for (int i = 0; i < strlen; i++) {
3338 final int c = s.charAt(i);3386 final int c = s.charAt(i);
3339 if (c <= 0x0001) {3387 if (c <= 0x0001) {
3340 _bytes[size++] = (byte) (0x01);3388 _bytes[size++] = (byte) (0x01);
3341 _bytes[size++] = (byte) (c + 0x0020);3389 _bytes[size++] = (byte) (c + 0x0020);
3342 } else if (c <= 0x007F) {3390 } else if (c <= 0x007F) {
3343 _bytes[size++] = (byte) c;3391 _bytes[size++] = (byte) c;
3344 } else if (c <= 0x07FF) {3392 } else if (c <= 0x07FF) {
3345 _bytes[size++] = (byte) (0xC0 | ((c >> 6) & 0x1F));3393 _bytes[size++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
3346 _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F));3394 _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F));
3347 } else {3395 } else {
3348 _bytes[size++] = (byte) (0xE0 | ((c >> 12) & 0x0F));3396 _bytes[size++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
3349 _bytes[size++] = (byte) (0x80 | ((c >> 6) & 0x3F));3397 _bytes[size++] = (byte) (0x80 | ((c >> 6) & 0x3F));
3350 _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F));3398 _bytes[size++] = (byte) (0x80 | ((c >> 0) & 0x3F));
3399 }
3351 }3400 }
3401 return endSegment(size);
3402 } catch (final ArrayIndexOutOfBoundsException e) {
3403 return tooLong(save);
3352 }3404 }
3353
3354 return endSegment(size);
3355 }3405 }
33563406
3357 /**3407 /**
@@ -3360,9 +3410,14 @@
3360 * @return This <code>Key</code>, to permit method call chaining3410 * @return This <code>Key</code>, to permit method call chaining
3361 */3411 */
3362 private Key appendNull() {3412 private Key appendNull() {
3363 int size = _size;3413 final int save = _size;
3364 _bytes[size++] = TYPE_NULL;3414 try {
3365 return endSegment(size);3415 int size = _size;
3416 _bytes[size++] = TYPE_NULL;
3417 return endSegment(size);
3418 } catch (final ArrayIndexOutOfBoundsException e) {
3419 return tooLong(save);
3420 }
3366 }3421 }
33673422
3368 private Key appendByKeyCoder(final Object object, final Class<?> cl, final KeyCoder coder,3423 private Key appendByKeyCoder(final Object object, final Class<?> cl, final KeyCoder coder,
@@ -3379,6 +3434,8 @@
3379 endSegment(_size);3434 endSegment(_size);
3380 size = _size;3435 size = _size;
3381 return this;3436 return this;
3437 } catch (final ArrayIndexOutOfBoundsException e) {
3438 return tooLong(size);
3382 } finally {3439 } finally {
3383 _size = size;3440 _size = size;
3384 _inKeyCoder = saveInKeyCoder;3441 _inKeyCoder = saveInKeyCoder;
@@ -3480,14 +3537,19 @@
3480 * level. The result key value should only be used in traversal operations.3537 * level. The result key value should only be used in traversal operations.
3481 */3538 */
3482 Key appendBefore() {3539 Key appendBefore() {
3483 int size = _size;3540 final int save = _size;
3484 _bytes[size++] = TYPE_BEFORE;3541 try {
3485 _bytes[size] = 0;3542 int size = _size;
3486 _size = size;3543 _bytes[size++] = TYPE_BEFORE;
3487 if (_depth != -1)3544 _bytes[size] = 0;
3488 _depth++;3545 _size = size;
3489 bumpGeneration();3546 if (_depth != -1)
3490 return this;3547 _depth++;
3548 bumpGeneration();
3549 return this;
3550 } catch (final ArrayIndexOutOfBoundsException e) {
3551 return tooLong(save);
3552 }
3491 }3553 }
34923554
3493 /**3555 /**
@@ -3496,34 +3558,54 @@
3496 * operations.3558 * operations.
3497 */3559 */
3498 Key appendAfter() {3560 Key appendAfter() {
3499 int size = _size;3561 final int save = _size;
3500 _bytes[size++] = (byte) TYPE_AFTER;3562 try {
3501 _bytes[size] = 0;3563 int size = _size;
3502 _size = size;3564 _bytes[size++] = (byte) TYPE_AFTER;
3503 if (_depth != -1)3565 _bytes[size] = 0;
3504 _depth++;3566 _size = size;
3505 bumpGeneration();3567 if (_depth != -1)
3506 return this;3568 _depth++;
3569 bumpGeneration();
3570 return this;
3571 } catch (final ArrayIndexOutOfBoundsException e) {
3572 return tooLong(save);
3573 }
3507 }3574 }
35083575
3509 private Key appendDate(final Date v) {3576 private Key appendDate(final Date v) {
3510 _bytes[_size++] = (byte) TYPE_DATE;3577 final int save = _size;
3511 final int size = appendLongInternal(v.getTime());3578 try {
3512 return endSegment(size);3579 _bytes[_size++] = (byte) TYPE_DATE;
3580 final int size = appendLongInternal(v.getTime());
3581 return endSegment(size);
3582 } catch (final ArrayIndexOutOfBoundsException e) {
3583 return tooLong(save);
3584 }
3513 }3585 }
35143586
3515 private Key appendBigInteger(final BigInteger v) {3587 private Key appendBigInteger(final BigInteger v) {
3516 _bytes[_size++] = TYPE_BIG_INTEGER;3588 final int save = _size;
3517 appendBigInteger(v, 0);3589 try {
3518 endSegment(_size);3590 _bytes[_size++] = TYPE_BIG_INTEGER;
3519 return this;3591 appendBigInteger(v, 0);
3592 endSegment(_size);
3593 return this;
3594 } catch (final ArrayIndexOutOfBoundsException e) {
3595 return tooLong(save);
3596 }
3520 }3597 }
35213598
3522 private Key appendBigDecimal(final BigDecimal v) {3599 private Key appendBigDecimal(final BigDecimal v) {
3523 _bytes[_size++] = TYPE_BIG_DECIMAL;3600 final int save = _size;
3524 appendBigInteger(v.unscaledValue(), v.scale());3601 try {
3525 endSegment(_size);3602 _bytes[_size++] = TYPE_BIG_DECIMAL;
3526 return this;3603 appendBigInteger(v.unscaledValue(), v.scale());
3604 endSegment(_size);
3605 return this;
3606 } catch (final ArrayIndexOutOfBoundsException e) {
3607 return tooLong(save);
3608 }
3527 }3609 }
35283610
3529 /**3611 /**
@@ -3541,12 +3623,17 @@
3541 * @return This <code>Key</code>, to permit method call chaining3623 * @return This <code>Key</code>, to permit method call chaining
3542 */3624 */
3543 public Key appendByteArray(final byte[] bytes, final int offset, final int size) {3625 public Key appendByteArray(final byte[] bytes, final int offset, final int size) {
3544 _bytes[_size++] = TYPE_BYTE_ARRAY;3626 final int save = _size;
3545 int keySize = _size;3627 try {
3546 System.arraycopy(bytes, offset, _bytes, keySize, size);3628 _bytes[_size++] = TYPE_BYTE_ARRAY;
3547 _size += size;3629 int keySize = _size;
3548 keySize += quoteNulls(keySize, size, false);3630 System.arraycopy(bytes, offset, _bytes, keySize, size);
3549 return endSegment(keySize);3631 _size += size;
3632 keySize += quoteNulls(keySize, size, false);
3633 return endSegment(keySize);
3634 } catch (final ArrayIndexOutOfBoundsException e) {
3635 return tooLong(save);
3636 }
3550 }3637 }
35513638
3552 private int getTypeCode() {3639 private int getTypeCode() {
@@ -3622,6 +3709,11 @@
3622 return this;3709 return this;
3623 }3710 }
36243711
3712 private Key tooLong(final int originalSize) throws KeyTooLongException {
3713 _size = originalSize;
3714 throw new KeyTooLongException("Maximum size=" + _maxSize + " original size=" + originalSize);
3715 }
3716
3625 private Key setRightEdge() {3717 private Key setRightEdge() {
3626 _bytes[0] = (byte) 255;3718 _bytes[0] = (byte) 255;
3627 _size = 1;3719 _size = 1;
36283720
=== modified file 'src/main/java/com/persistit/Persistit.java'
--- src/main/java/com/persistit/Persistit.java 2013-03-13 15:55:19 +0000
+++ src/main/java/com/persistit/Persistit.java 2013-03-20 19:33:22 +0000
@@ -1691,7 +1691,7 @@
1691 releaseAllResources();1691 releaseAllResources();
1692 }1692 }
16931693
1694 private void closeZombieTransactions(boolean removeAllSessions) {1694 private void closeZombieTransactions(final boolean removeAllSessions) {
1695 final Set<SessionId> sessionIds;1695 final Set<SessionId> sessionIds;
1696 synchronized (_transactionSessionMap) {1696 synchronized (_transactionSessionMap) {
1697 sessionIds = new HashSet<SessionId>(_transactionSessionMap.keySet());1697 sessionIds = new HashSet<SessionId>(_transactionSessionMap.keySet());
16981698
=== modified file 'src/main/java/com/persistit/Transaction.java'
--- src/main/java/com/persistit/Transaction.java 2013-03-13 16:27:04 +0000
+++ src/main/java/com/persistit/Transaction.java 2013-03-20 19:33:22 +0000
@@ -685,7 +685,7 @@
685 }685 }
686 try {686 try {
687 pruneLockPages();687 pruneLockPages();
688 } catch (Exception e) {688 } catch (final Exception e) {
689 _persistit.getLogBase().pruneException.log(e, "locks");689 _persistit.getLogBase().pruneException.log(e, "locks");
690 }690 }
691 _transactionStatus = null;691 _transactionStatus = null;
692692
=== added file 'src/main/java/com/persistit/exception/KeyTooLongException.java'
--- src/main/java/com/persistit/exception/KeyTooLongException.java 1970-01-01 00:00:00 +0000
+++ src/main/java/com/persistit/exception/KeyTooLongException.java 2013-03-20 19:33:22 +0000
@@ -0,0 +1,39 @@
1/**
2 * Copyright © 2005-2012 Akiban Technologies, Inc. All rights reserved.
3 *
4 * This program and the accompanying materials are made available
5 * under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * This program may also be available under different license terms.
10 * For more information, see www.akiban.com or contact licensing@akiban.com.
11 *
12 * Contributors:
13 * Akiban Technologies, Inc.
14 */
15
16package com.persistit.exception;
17
18/**
19 * Thrown by {@link com.persistit.Key} when an attempt to append a key segment
20 * exceeds the maximum size of the key.
21 *
22 * @version 1.0
23 */
24public class KeyTooLongException extends RuntimeException {
25
26 private static final long serialVersionUID = -4657691754665822537L;
27
28 public KeyTooLongException() {
29 super();
30 }
31
32 public KeyTooLongException(final String msg) {
33 super(msg);
34 }
35
36 public KeyTooLongException(final Throwable t) {
37 super(t);
38 }
39}
040
=== modified file 'src/test/java/com/persistit/TransactionAbandonedTest.java'
--- src/test/java/com/persistit/TransactionAbandonedTest.java 2013-02-20 17:13:17 +0000
+++ src/test/java/com/persistit/TransactionAbandonedTest.java 2013-03-20 19:33:22 +0000
@@ -15,13 +15,14 @@
1515
16package com.persistit;16package com.persistit;
1717
18import static org.junit.Assert.assertEquals;
19
20import org.junit.Before;
21import org.junit.Test;
22
18import com.persistit.exception.PersistitException;23import com.persistit.exception.PersistitException;
19import com.persistit.unit.ConcurrentUtil;24import com.persistit.unit.ConcurrentUtil;
20import com.persistit.unit.UnitTestProperties;25import com.persistit.unit.UnitTestProperties;
21import org.junit.Before;
22import org.junit.Test;
23
24import static org.junit.Assert.assertEquals;
2526
26/**27/**
27 * <p>28 * <p>
@@ -51,7 +52,7 @@
51 private final boolean doRead;52 private final boolean doRead;
52 private final boolean doWrite;53 private final boolean doWrite;
5354
54 public TxnAbandoner(Persistit persistit, boolean doRead, boolean doWrite) {55 public TxnAbandoner(final Persistit persistit, final boolean doRead, final boolean doWrite) {
55 this.persistit = persistit;56 this.persistit = persistit;
56 this.doRead = doRead;57 this.doRead = doRead;
57 this.doWrite = doWrite;58 this.doWrite = doWrite;
@@ -59,7 +60,7 @@
5960
60 @Override61 @Override
61 public void run() throws PersistitException {62 public void run() throws PersistitException {
62 Transaction txn = persistit.getTransaction();63 final Transaction txn = persistit.getTransaction();
63 txn.begin();64 txn.begin();
64 if (doRead) {65 if (doRead) {
65 assertEquals("Traverse count", KEY_RANGE, scanAndCount(getExchange(persistit)));66 assertEquals("Traverse count", KEY_RANGE, scanAndCount(getExchange(persistit)));
@@ -70,18 +71,19 @@
70 }71 }
71 }72 }
7273
73 private static Exchange getExchange(Persistit persistit) throws PersistitException {74 private static Exchange getExchange(final Persistit persistit) throws PersistitException {
74 return persistit.getExchange(UnitTestProperties.VOLUME_NAME, TREE, true);75 return persistit.getExchange(UnitTestProperties.VOLUME_NAME, TREE, true);
75 }76 }
7677
77 private static void loadData(Persistit persistit, int keyOffset, int count) throws PersistitException {78 private static void loadData(final Persistit persistit, final int keyOffset, final int count)
78 Exchange ex = getExchange(persistit);79 throws PersistitException {
80 final Exchange ex = getExchange(persistit);
79 for (int i = 0; i < count; ++i) {81 for (int i = 0; i < count; ++i) {
80 ex.clear().append(keyOffset + i).store();82 ex.clear().append(keyOffset + i).store();
81 }83 }
82 }84 }
8385
84 private static int scanAndCount(Exchange ex) throws PersistitException {86 private static int scanAndCount(final Exchange ex) throws PersistitException {
85 ex.clear().append(Key.BEFORE);87 ex.clear().append(Key.BEFORE);
86 int saw = 0;88 int saw = 0;
87 while (ex.next()) {89 while (ex.next()) {
@@ -96,8 +98,8 @@
96 loadData(_persistit, KEY_START, KEY_RANGE);98 loadData(_persistit, KEY_START, KEY_RANGE);
97 }99 }
98100
99 private void runAndCleanup(String name, boolean doRead, boolean doWrite) {101 private void runAndCleanup(final String name, final boolean doRead, final boolean doWrite) {
100 Thread t = ConcurrentUtil.createThread(name, new TxnAbandoner(_persistit, false, false));102 final Thread t = ConcurrentUtil.createThread(name, new TxnAbandoner(_persistit, false, false));
101 ConcurrentUtil.startAndJoinAssertSuccess(MAX_TIMEOUT_MS, t);103 ConcurrentUtil.startAndJoinAssertSuccess(MAX_TIMEOUT_MS, t);
102 // Threw exception before fix104 // Threw exception before fix
103 _persistit.cleanup();105 _persistit.cleanup();
@@ -119,11 +121,11 @@
119 runAndCleanup("ReadAndWrite", true, true);121 runAndCleanup("ReadAndWrite", true, true);
120 assertEquals("Traversed after abandoned", KEY_RANGE, scanAndCount(getExchange(_persistit)));122 assertEquals("Traversed after abandoned", KEY_RANGE, scanAndCount(getExchange(_persistit)));
121 // Check that the abandoned was pruned123 // Check that the abandoned was pruned
122 CleanupManager cm = _persistit.getCleanupManager();124 final CleanupManager cm = _persistit.getCleanupManager();
123 for (int i = 0; i < 5 && cm.getEnqueuedCount() > 0; ++i) {125 for (int i = 0; i < 5 && cm.getEnqueuedCount() > 0; ++i) {
124 cm.runTask();126 cm.runTask();
125 }127 }
126 Exchange rawEx = getExchange(_persistit);128 final Exchange rawEx = getExchange(_persistit);
127 rawEx.ignoreMVCCFetch(true);129 rawEx.ignoreMVCCFetch(true);
128 assertEquals("Raw traversed after abandoned", KEY_RANGE, scanAndCount(rawEx));130 assertEquals("Raw traversed after abandoned", KEY_RANGE, scanAndCount(rawEx));
129 }131 }
130132
=== modified file 'src/test/java/com/persistit/unit/ExchangeTest.java'
--- src/test/java/com/persistit/unit/ExchangeTest.java 2012-12-24 18:27:56 +0000
+++ src/test/java/com/persistit/unit/ExchangeTest.java 2013-03-20 19:33:22 +0000
@@ -31,7 +31,7 @@
31import com.persistit.PersistitUnitTestCase;31import com.persistit.PersistitUnitTestCase;
32import com.persistit.Transaction;32import com.persistit.Transaction;
33import com.persistit.Volume;33import com.persistit.Volume;
34import com.persistit.exception.ConversionException;34import com.persistit.exception.KeyTooLongException;
35import com.persistit.exception.PersistitException;35import com.persistit.exception.PersistitException;
3636
37public class ExchangeTest extends PersistitUnitTestCase {37public class ExchangeTest extends PersistitUnitTestCase {
@@ -151,8 +151,8 @@
151 randomKey = createString(initialLength);151 randomKey = createString(initialLength);
152 try {152 try {
153 ex.clear().append(randomKey);153 ex.clear().append(randomKey);
154 fail("ConversionException should have been thrown");154 fail("KeyTooLongException should have been thrown");
155 } catch (final ConversionException expected) {155 } catch (final KeyTooLongException expected) {
156 }156 }
157 }157 }
158158
159159
=== modified file 'src/test/java/com/persistit/unit/KeyTest1.java'
--- src/test/java/com/persistit/unit/KeyTest1.java 2012-11-07 14:03:51 +0000
+++ src/test/java/com/persistit/unit/KeyTest1.java 2013-03-20 19:33:22 +0000
@@ -22,6 +22,7 @@
2222
23import java.math.BigDecimal;23import java.math.BigDecimal;
24import java.math.BigInteger;24import java.math.BigInteger;
25import java.util.Date;
2526
26import junit.framework.Assert;27import junit.framework.Assert;
2728
@@ -33,8 +34,8 @@
33import com.persistit.PersistitUnitTestCase;34import com.persistit.PersistitUnitTestCase;
34import com.persistit.TestShim;35import com.persistit.TestShim;
35import com.persistit.exception.InvalidKeyException;36import com.persistit.exception.InvalidKeyException;
37import com.persistit.exception.KeyTooLongException;
36import com.persistit.exception.MissingKeySegmentException;38import com.persistit.exception.MissingKeySegmentException;
37import com.persistit.util.Util;
3839
39public class KeyTest1 extends PersistitUnitTestCase {40public class KeyTest1 extends PersistitUnitTestCase {
4041
@@ -45,22 +46,26 @@
45 private double dv1;46 private double dv1;
46 private double dv2;47 private double dv2;
4748
48 long[] TEST_LONGS = { 0, 1, 2, 3, 123, 126, 127, 128, 129, 130, 131, 132, 250, 251, 252, 253, 254, 255, 256, 257,49 private final static long[] TEST_LONGS = { 0, 1, 2, 3, 123, 126, 127, 128, 129, 130, 131, 132, 250, 251, 252, 253,
49 258, 259, 260, 4094, 4095, 4096, 4097, 4098, 16383, 16384, 16385, 0x1FFFFEL, 0x1FFFFFL, 0x200000L,50 254, 255, 256, 257, 258, 259, 260, 4094, 4095, 4096, 4097, 4098, 16383, 16384, 16385, 0x1FFFFEL, 0x1FFFFFL,
50 0x3FFFFFFFFFEL, 0x3FFFFFFFFFFL, 0x2000000000000L, 0x1FFFFFFFFFFFFL, 0x1FFFFFFFFFFFEL,51 0x200000L, 0x3FFFFFFFFFEL, 0x3FFFFFFFFFFL, 0x2000000000000L, 0x1FFFFFFFFFFFFL, 0x1FFFFFFFFFFFEL,
51 Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1, Integer.MAX_VALUE + 0, Integer.MAX_VALUE + 1,52 Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1, Integer.MAX_VALUE + 0, Integer.MAX_VALUE + 1,
52 Integer.MAX_VALUE + 2, Integer.MAX_VALUE + 3, Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2,53 Integer.MAX_VALUE + 2, Integer.MAX_VALUE + 3, Long.MIN_VALUE, Long.MIN_VALUE + 1, Long.MIN_VALUE + 2,
53 Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, };54 Long.MAX_VALUE, Long.MAX_VALUE - 1, Long.MAX_VALUE - 2, };
5455
55 float[] TEST_FLOATS = { 0.0F, 1.0F, 2.0F, 12345.0F, 0.0003F, 1.2345E-10F, 1.12345E-20F, 0.0F, -1.0F, -2.0F,56 private final static float[] TEST_FLOATS = { 0.0F, 1.0F, 2.0F, 12345.0F, 0.0003F, 1.2345E-10F, 1.12345E-20F, 0.0F,
56 -12345.0F, -0.0003F, -1.2345E-10F, -1.12345E-20F, Float.MAX_VALUE, Float.MIN_VALUE, Float.MAX_VALUE / 2.0F,57 -1.0F, -2.0F, -12345.0F, -0.0003F, -1.2345E-10F, -1.12345E-20F, Float.MAX_VALUE, Float.MIN_VALUE,
57 Float.MAX_VALUE / 3.0F, Float.MAX_VALUE / 4.0F, Float.MIN_VALUE / 2.0F, Float.MIN_VALUE / 3.0F,58 Float.MAX_VALUE / 2.0F, Float.MAX_VALUE / 3.0F, Float.MAX_VALUE / 4.0F, Float.MIN_VALUE / 2.0F,
58 Float.MIN_VALUE / 4.0F, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY };59 Float.MIN_VALUE / 3.0F, Float.MIN_VALUE / 4.0F, Float.NaN, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY };
5960
60 double[] TEST_DOUBLES = { 0.0, 1.0, 2.0, 12345.0, 0.0003, 1.2345E-10, 1.12345E-20, 0.0, -1.0, -2.0, -12345.0,61 private final static double[] TEST_DOUBLES = { 0.0, 1.0, 2.0, 12345.0, 0.0003, 1.2345E-10, 1.12345E-20, 0.0, -1.0,
61 -0.0003, -1.2345E-10, -1.12345E-20, Double.MAX_VALUE, Double.MIN_VALUE, Double.MAX_VALUE / 2.0,62 -2.0, -12345.0, -0.0003, -1.2345E-10, -1.12345E-20, Double.MAX_VALUE, Double.MIN_VALUE,
62 Double.MAX_VALUE / 3.0, Double.MAX_VALUE / 4.0, Double.MIN_VALUE / 2.0, Double.MIN_VALUE / 3.0,63 Double.MAX_VALUE / 2.0, Double.MAX_VALUE / 3.0, Double.MAX_VALUE / 4.0, Double.MIN_VALUE / 2.0,
63 Double.MIN_VALUE / 4.0, Double.NaN, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY };64 Double.MIN_VALUE / 3.0, Double.MIN_VALUE / 4.0, Double.NaN, Double.NEGATIVE_INFINITY,
65 Double.POSITIVE_INFINITY };
66
67 private final static Object[] TEST_OBJECTS = new Object[] { null, true, false, (byte) 1, (char) 2, (short) 3, 4,
68 (long) 5, 6.0f, 7.0d, "This is a String", new Date(), new BigInteger("1"), new BigDecimal("2.2") };
6469
65 @Test70 @Test
66 public void test1() {71 public void test1() {
@@ -1013,6 +1018,24 @@
1013 assertEquals("Key value incorrect", "{1,2,3,\"abc\",\"abc\"}", key1.toString());1018 assertEquals("Key value incorrect", "{1,2,3,\"abc\",\"abc\"}", key1.toString());
1014 }1019 }
10151020
1021 @Test
1022 public void testKeyTooLong() throws Exception {
1023 for (int maxSize = 1; maxSize < 99; maxSize++) {
1024 mainLoop: for (final Object value : TEST_OBJECTS) {
1025 final Key key = new Key(_persistit, maxSize);
1026 // Every appended value consumes at least 2 bytes
1027 for (int i = 0; i < 50; i++) {
1028 try {
1029 key.append(value);
1030 } catch (final KeyTooLongException e) {
1031 continue mainLoop;
1032 }
1033 }
1034 fail("Should have thrown a KeyTooLongException for maxSize=" + maxSize + " and value " + value);
1035 }
1036 }
1037 }
1038
1016 private static boolean doubleEquals(final double f1, final double f2) {1039 private static boolean doubleEquals(final double f1, final double f2) {
1017 if (Double.isNaN(f1)) {1040 if (Double.isNaN(f1)) {
1018 return Double.isNaN(f2);1041 return Double.isNaN(f2);
@@ -1023,45 +1046,6 @@
1023 return f1 == f2;1046 return f1 == f2;
1024 }1047 }
10251048
1026 @Override
1027 public void runAllTests() throws Exception {
1028 test1();
1029 test2();
1030 test3();
1031 test4();
1032 test5();
1033 test6();
1034 test7();
1035 test8();
1036 test9();
1037 test10();
1038 test11();
1039 test12();
1040 test13();
1041 test14();
1042 }
1043
1044 private String floatBits(final float v) {
1045 final int bits = Float.floatToIntBits(v);
1046 final StringBuilder sb = new StringBuilder();
1047 Util.hex(sb, bits, 8);
1048 return sb.toString();
1049 }
1050
1051 private String doubleBits(final double v) {
1052 final long bits = Double.doubleToLongBits(v);
1053 final StringBuilder sb = new StringBuilder();
1054 Util.hex(sb, bits, 16);
1055 return sb.toString();
1056 }
1057
1058 private void debug(final boolean condition) {
1059 if (!condition) {
1060 return;
1061 }
1062 return; // <-- breakpoint here
1063 }
1064
1065 private Key newKey() {1049 private Key newKey() {
1066 return new Key(_persistit);1050 return new Key(_persistit);
1067 }1051 }

Subscribers

People subscribed via source and target branches