From: <tho...@us...> - 2011-03-18 14:34:08
|
Revision: 4317 http://bigdata.svn.sourceforge.net/bigdata/?rev=4317&view=rev Author: thompsonbry Date: 2011-03-18 14:34:01 +0000 (Fri, 18 Mar 2011) Log Message: ----------- Added BytesUtil#fromString(String s) method to decode an unsigned byte[] as generated by BytesUtil#toString(byte[]). Unit tests for the same. Modified Paths: -------------- trunk/bigdata/src/java/com/bigdata/btree/BytesUtil.java trunk/bigdata/src/test/com/bigdata/btree/TestBytesUtil.java Modified: trunk/bigdata/src/java/com/bigdata/btree/BytesUtil.java =================================================================== --- trunk/bigdata/src/java/com/bigdata/btree/BytesUtil.java 2011-03-18 14:33:14 UTC (rev 4316) +++ trunk/bigdata/src/java/com/bigdata/btree/BytesUtil.java 2011-03-18 14:34:01 UTC (rev 4317) @@ -33,6 +33,7 @@ import org.apache.log4j.Logger; +import com.bigdata.btree.keys.SuccessorUtil; import com.bigdata.rawstore.Bytes; /** @@ -66,7 +67,7 @@ */ public class BytesUtil { - protected static final transient Logger log = Logger.getLogger(BytesUtil.class); + private static final transient Logger log = Logger.getLogger(BytesUtil.class); /** * An empty <code>byte[]</code>. @@ -437,6 +438,8 @@ * A variable length unsigned byte array. * * @return A new unsigned byte[] that is the successor of the key. + * + * @see SuccessorUtil#successor(byte[]) */ public final static byte[] successor(final byte[] key) { @@ -458,10 +461,10 @@ * separator key that is greater than or equal to the search key. * </p> * <p> - * Separator keys separate leaves and must be choosen with that purpose in + * Separator keys separate leaves and must be chosen with that purpose in * mind. The simplest way to choose the separator key is to just take the * first key of the leaf - this is always correct. However, shorter - * separator keys may be choosen by defining the separator key as the + * separator keys may be chosen by defining the separator key as the * shortest key that is less than or equal to the first key of a leaf and * greater than the last key of the left sibling of that leaf (that is, the * key for the entry that immediately proceeds the first entry on the leaf). @@ -619,7 +622,7 @@ } - private static transient String NULL = "null"; + private static transient final String NULL = "null"; /** * Formats the data into a {@link String}. @@ -655,7 +658,112 @@ return sb.toString(); } + + /** + * Decode a string representation of an <code>unsigned byte[]</code> as + * generated by {@link #toString()}. This is mainly useful when decoding + * keys in a log file. Acceptable inputs have the following general form: + * + * <pre> + * array := '[' v tail ']' + * v := 0-255 + * tail := ( empty | ',' v tail ) + * </pre> + * + * @param a + * The representation of the <code>unsigned byte[]</code>. + * + * @return The <code>unsigned byte[]</code> + * + * @throws IllegalArgumentException + * if the argument is <code>null</code>. + * @throws NumberFormatException + * if the values in the array are not integers. + * @throws NumberFormatException + * if the values in the array lie outside of the numeric range + * of an unsigned byte. + */ + static public byte[] fromString( String s) { + + if(s == null) + throw new IllegalArgumentException(); + + final int end = s.indexOf(']'); + + if (end == -1) + throw new IllegalArgumentException(); + + int start = s.indexOf('['); + + if (start == -1) + throw new IllegalArgumentException(); + + start++; + + if (start > end) + throw new IllegalArgumentException(); + + while (Character.isWhitespace(s.charAt(start)) && start < end) { + start++; + } + + if (start + 1 >= end) + return EMPTY; + + s = s.substring(start, end); + + final String[] t = comma.split(s); + final int n = t.length; +// System.err.println("input: '" + s + "'"); +// System.err.println("n: " + n); + +// final Matcher m = arrayPattern.matcher(s); +// if (!m.matches()) +// throw new NumberFormatException(); +// final int n = m.groupCount(); +// System.err.println("input: '" + s+"'"); +// System.err.println("ngroups=" + n + ", matcher: " + m); + + if (n == 0) + return EMPTY; + + final byte[] a = new byte[n]; + + for (int i = 0; i < n; i++) { + +// final String x = m.group(i + 1); + + final String x = t[i].trim(); + +// System.err.print("'"+x+"'"); + + final int j = Integer.parseInt(x); + +// System.err.print("("+j+")"); + + if (j < 0 || j > 255) + throw new NumberFormatException("Not an unsigned byte: " + x); + + final byte b = (byte) (j & 0xff); + +// System.err.println("[" + b + "]"); + + a[i] = b; + + } + + return a; + + } + private static final Pattern comma = Pattern.compile(","); + +// private static final Pattern arrayPattern = Pattern.compile(// +//// "(?:\\w*[\\w*)" + // +// "(\\d+(?:,\\w)?)"// +//// "(?:\\w*]\\w*)" // +// ); + /** * Binary search on an array whose members are variable length unsigned * byte[]s. Modified: trunk/bigdata/src/test/com/bigdata/btree/TestBytesUtil.java =================================================================== --- trunk/bigdata/src/test/com/bigdata/btree/TestBytesUtil.java 2011-03-18 14:33:14 UTC (rev 4316) +++ trunk/bigdata/src/test/com/bigdata/btree/TestBytesUtil.java 2011-03-18 14:34:01 UTC (rev 4317) @@ -27,6 +27,8 @@ package com.bigdata.btree; +import java.util.Arrays; + import junit.framework.TestCase2; import com.bigdata.btree.keys.IKeyBuilder; @@ -855,4 +857,154 @@ } + /** + * Unit test for {@link BytesUtil#toString(byte[])} based on known good + * data. + */ + public void test_toString() { + + final byte[] a = new byte[] { 0, 1, 2, 3, 4, 126, 127, -1, -2, -3, -4, + -126, -127, -128 }; + + final String s = BytesUtil.toString(a); + + System.err.println(s); + + final String expected = "[0, 1, 2, 3, 4, 126, 127, 255, 254, 253, 252, 130, 129, 128]"; +// final String expected = "[-128, -127, -126, -125, -124, -2, -1, 127, 126, 125, 124, 2, 1, 0]"; + + assertEquals(expected, s); + + } + + /** + * This does some order checking, but it also provides a visual check on + * {@link BytesUtil#toString(byte[])}. The "Unsigned" representation should + * be strictly ascending from <code>[0]</code> through <code>255</code>. + * + * @todo verify that using direct parse. + */ + public void test_order() { + + byte[] a = new byte[1]; + + byte[] last = new byte[1]; + + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + + // generate next array value + a[0] = KeyBuilder.encodeByte(i); + + System.err.println("S: " + Arrays.toString(a) + " => U:" + + BytesUtil.toString(a)); + + if (i > 0) { + + // ordering test + assertTrue(BytesUtil.compareBytes(last, a) < 0); + + // ordering test + assertTrue(BytesUtil.UnsignedByteArrayComparator.INSTANCE + .compare(last, a) < 0); + + } + + // note the last array value (it's just one byte). + last[0] = a[0]; + + } + + System.err.println("S: " + Arrays.toString(a) + " => U:" + + BytesUtil.toString(a)); + + } + + /** + * Unit test for {@link BytesUtil#fromString(String)} based on known good + * data. + */ + public void test_fromString() { + + final byte[] expected = new byte[] { 0, 1, 2, 3, 4, 126, 127, -1, -2, -3, -4, -126, -127, -128 }; + +// final byte[] expected = new byte[] {-128, -127, -126, -125, -124, -2, -1, 127, 126, 125, 124, 2, 1, 0}; + + final String s = "[0, 1, 2, 3, 4, 126, 127, 255, 254, 253, 252, 130, 129, 128]"; + + // '0'(0)[-128]'1'(1)[-127]'2'(2)[-126]'3'(3)[-125]'4'(4)[-124]'126'(126)[-2]'127'(127)[-1]'255'(255)[127]'254'(254)[126]'253'(253)[125]'252'(252)[124]'130'(130)[2]'129'(129)[1]'128'(128)[0] + + final byte[] actual = BytesUtil.fromString(s); + + assertEquals(expected, actual); + + } + + /** + * Unit test for {@link BytesUtil#fromString(String)} when given an empty + * array as input together with legal whitespace variants of an empty array. + */ + public void test_fromString_emptyArray() { + + final byte[] expected = new byte[] {}; + + assertEquals(expected,BytesUtil.fromString("[]")); + assertEquals(expected,BytesUtil.fromString("[ ]")); + assertEquals(expected,BytesUtil.fromString(" [] ")); + assertEquals(expected,BytesUtil.fromString(" [ ] ")); + assertEquals(expected,BytesUtil.fromString(" [ ] ")); + + } + + /** + * Unit test for {@link BytesUtil#fromString(String)} with a + * <code>null</code> argument. + */ + public void test_fromString_correctRejection_null() { + + try { + BytesUtil.fromString(null); + fail("Expecting: " + IllegalArgumentException.class); + } catch (IllegalArgumentException t) { + if (log.isInfoEnabled()) + log.info("Ignoring expected exception: " + t); + } + + } + + /** + * Correct rejection test for {@link BytesUtil#fromString(String)} when the + * data contains values which lie outside of the range of an + * <code>unsigned byte</code>. + */ + public void test_fromString_correctRejection_badRange() { + + // outside of range [0:255] + try { + BytesUtil.fromString("[256]"); + fail("Expecting: " + IllegalArgumentException.class); + } catch (IllegalArgumentException t) { + if (log.isInfoEnabled()) + log.info("Ignoring expected exception: " + t); + } + + // outside of range [0:255] + try { + BytesUtil.fromString("[-1]"); + fail("Expecting: " + IllegalArgumentException.class); + } catch (IllegalArgumentException t) { + if (log.isInfoEnabled()) + log.info("Ignoring expected exception: " + t); + } + + // not an integer + try { + BytesUtil.fromString("[1a]"); + fail("Expecting: " + IllegalArgumentException.class); + } catch (IllegalArgumentException t) { + if (log.isInfoEnabled()) + log.info("Ignoring expected exception: " + t); + } + + } + } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |