closes #827 - implement a copyTo (#841)
diff --git a/modules/api/src/main/java/org/apache/fluo/api/data/Bytes.java b/modules/api/src/main/java/org/apache/fluo/api/data/Bytes.java
index 1e66a22..c490329 100644
--- a/modules/api/src/main/java/org/apache/fluo/api/data/Bytes.java
+++ b/modules/api/src/main/java/org/apache/fluo/api/data/Bytes.java
@@ -565,4 +565,45 @@
public static BytesBuilder builder(int initialCapacity) {
return new BytesBuilder(initialCapacity);
}
+
+ /**
+ * Copy entire Bytes object to specific byte array. Uses the specified offset in the dest byte
+ * array to start the copy.
+ *
+ * @param dest destination array
+ * @param destPos starting position in the destination data.
+ * @exception IndexOutOfBoundsException if copying would cause access of data outside array
+ * bounds.
+ * @exception NullPointerException if either <code>src</code> or <code>dest</code> is
+ * <code>null</code>.
+ * @since 1.1.0
+ */
+ public void copyTo(byte[] dest, int destPos) {
+ arraycopy(0, dest, destPos, this.length);
+ }
+
+ /**
+ * Copy a subsequence of Bytes to specific byte array. Uses the specified offset in the dest byte
+ * array to start the copy.
+ *
+ * @param start index of subsequence start (inclusive)
+ * @param end index of subsequence end (exclusive)
+ * @param dest destination array
+ * @param destPos starting position in the destination data.
+ * @exception IndexOutOfBoundsException if copying would cause access of data outside array
+ * bounds.
+ * @exception NullPointerException if either <code>src</code> or <code>dest</code> is
+ * <code>null</code>.
+ * @since 1.1.0
+ */
+ public void copyTo(int start, int end, byte[] dest, int destPos) {
+ // this.subSequence(start, end).copyTo(dest, destPos) would allocate another Bytes object
+ arraycopy(start, dest, destPos, end - start);
+ }
+
+ private void arraycopy(int start, byte[] dest, int destPos, int length) {
+ // since dest is byte[], we can't get the ArrayStoreException
+ System.arraycopy(this.data, start + this.offset, dest, destPos, length);
+ }
+
}
diff --git a/modules/api/src/test/java/org/apache/fluo/api/data/BytesTest.java b/modules/api/src/test/java/org/apache/fluo/api/data/BytesTest.java
index 7d3aff9..aa5c2b6 100644
--- a/modules/api/src/test/java/org/apache/fluo/api/data/BytesTest.java
+++ b/modules/api/src/test/java/org/apache/fluo/api/data/BytesTest.java
@@ -15,6 +15,8 @@
package org.apache.fluo.api.data;
+import static org.junit.Assert.fail;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -22,7 +24,6 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
-import org.apache.fluo.api.data.Bytes;
import org.junit.Assert;
import org.junit.Test;
@@ -252,4 +253,103 @@
Assert.assertSame(s1, b1.toString());
Assert.assertSame(s2, b2.toString());
}
+
+ @Test
+ public void testCopyTo() {
+ Bytes field1 = Bytes.of("foo");
+ Bytes field2 = Bytes.of("bar");
+
+ byte[] dest = new byte[field1.length() + field2.length() + 1];
+
+ field1.copyTo(dest, 0);
+ dest[field1.length()] = ':';
+ field2.copyTo(dest, field1.length() + 1);
+
+ Assert.assertEquals("foo:bar", new String(dest));
+ }
+
+ @Test
+ public void testCopyToOutOfBounds() {
+ Bytes field = Bytes.of("abcdefg");
+ byte[] dest = new byte[field.length() - 1];
+ String initialDest = new String(dest);
+
+ try {
+ field.copyTo(dest, 0);
+ fail("Should not get here");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // dest should not have changed
+ Assert.assertEquals(new String(dest), initialDest);
+ }
+ }
+
+ @Test
+ public void testCopyToSubset() {
+ Bytes field = Bytes.of("abcdefg");
+ byte[] dest = new byte[4];
+
+ field.copyTo(3, 6, dest, 0);
+ String expected = "def\0";
+ String actual = new String(dest);
+
+ Assert.assertEquals(expected, actual);
+
+ field.subSequence(3, 6).copyTo(dest, 1);
+ // because offset was 1, it will replace ef\0 with def and leave d at position 0
+ Assert.assertEquals("ddef", new String(dest));
+ }
+
+ @Test
+ public void testCopyToArgsReversed() {
+ Bytes field = Bytes.of("abcdefg");
+ byte[] dest = new byte[4];
+
+ try {
+ field.copyTo(6, 3, dest, 0);
+ fail("should not get here");
+ } catch (java.lang.ArrayIndexOutOfBoundsException e) {
+ Assert.assertEquals("\0\0\0\0", new String(dest));
+ }
+ }
+
+ @Test
+ public void testCopyToNothing() {
+ Bytes field = Bytes.of("abcdefg");
+ byte[] dest = new byte[4];
+
+ field.copyTo(3, 3, dest, 0);
+ // should not have changed
+ Assert.assertEquals("\0\0\0\0", new String(dest));
+ }
+
+ @Test
+ public void testCopyToWithUnicode() {
+ // first observe System.arraycopy
+ String begin = "abc"; // 3 chars, 3 bytes
+ String mid1 = "†"; // 1 char, 3 bytes
+ String mid2 = "𝔊"; // 2 chars, 4 bytes
+ String end = "efghi"; // 5 chars, 5 bytes
+ Assert.assertEquals(11, begin.length() + mid1.length() + mid2.length() + end.length());
+
+ byte[] copyFrom = (begin + mid1 + mid2 + end).getBytes();
+ //@formatter:off
+ // [ a, b, c, †, 𝔊, e, f, g, h, i]
+ // [97, 98, 99, -30, -128, -96, -16, -99, -108, -118, 101, 102, 103, 104, 105]
+ //@formatter:on
+ Assert.assertEquals(15, copyFrom.length);
+
+ byte[] copyTo = new byte[9];
+ System.arraycopy(copyFrom, 2, copyTo, 0, 9);
+ Assert.assertEquals("c†𝔊e", new String(copyTo));
+
+ // now make a Bytes out of the craziness
+ Bytes allBytes = Bytes.of(copyFrom);
+ Assert.assertEquals(15, allBytes.length());
+
+ // and test Bytes.arraycopy works the same
+ byte[] copyTo2 = new byte[9];
+ allBytes.copyTo(2, 11, copyTo2, 0);
+ Assert.assertEquals("c†𝔊e", new String(copyTo2));
+ }
+
}