Repository: amzn/ion-java
Pull request #257 (2019-07-24 15:47:04)

Add IonSystem#singleValue(byte[], int, int)
When ion object resides in the middle of bytearray, this helper
method would help to extract it without memory copy overhead.

Issue #256

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.


codecov[bot]: # [Codecov](https://codecov.io/gh/amzn/ion-java/pull/257?src=pr&el=h1) Report
> Merging [#257](https://codecov.io/gh/amzn/ion-java/pull/257?src=pr&el=desc) into [master](https://codecov.io/gh/amzn/ion-java/commit/157d47441df57cd11b8fc10526142ff798e44a08?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/amzn/ion-java/pull/257/graphs/tree.svg?width=650&token=M93Y3e3kKn&height=150&src=pr)](https://codecov.io/gh/amzn/ion-java/pull/257?src=pr&el=tree)

```diff
@@             Coverage Diff              @@
##             master     #257      +/-   ##
============================================
+ Coverage     63.83%   63.84%   +<.01%     
- Complexity     4796     4799       +3     
============================================
  Files           136      136              
  Lines         20997    20998       +1     
  Branches       3821     3821              
============================================
+ Hits          13404    13406       +2     
  Misses         6249     6249              
+ Partials       1344     1343       -1
```


| [Impacted Files](https://codecov.io/gh/amzn/ion-java/pull/257?src=pr&el=tree) | Coverage Δ | Complexity Δ | |
|---|---|---|---|
| [src/com/amazon/ion/impl/lite/IonSystemLite.java](https://codecov.io/gh/amzn/ion-java/pull/257/diff?src=pr&el=tree#diff-c3JjL2NvbS9hbWF6b24vaW9uL2ltcGwvbGl0ZS9Jb25TeXN0ZW1MaXRlLmphdmE=) | `77.65% <100%> (+0.08%)` | `93 <2> (+1)` | :arrow_up: |
| [src/com/amazon/ion/impl/BlockedBuffer.java](https://codecov.io/gh/amzn/ion-java/pull/257/diff?src=pr&el=tree#diff-c3JjL2NvbS9hbWF6b24vaW9uL2ltcGwvQmxvY2tlZEJ1ZmZlci5qYXZh) | `51.09% <0%> (+0.12%)` | `80% <0%> (+2%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/amzn/ion-java/pull/257?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/amzn/ion-java/pull/257?src=pr&el=footer). Last update [157d474...aa9aa9e](https://codecov.io/gh/amzn/ion-java/pull/257?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/com/amazon/ion/IonSystem.java b/src/com/amazon/ion/IonSystem.java
index 56871c9e..f7bb29c0 100644
--- a/src/com/amazon/ion/IonSystem.java
+++ b/src/com/amazon/ion/IonSystem.java
@@ -373,6 +373,27 @@ public interface IonSystem
      */
     public IonValue singleValue(byte[] ionData);
 
+    /**
+     * Extracts a single value from Ion text or binary data.
+     * <p>
+     * This method will auto-detect and uncompress GZIPped Ion data.
+     *
+     * @param ionData is used only within the range of bytes starting at
+     * {@code offset} for {@code len} bytes.
+     * The data in that range may be either Ion binary data, or UTF-8 Ion text.
+     * @param offset must be non-negative and less than {@code ionData.length}.
+     * @param len must be non-negative and {@code offset+len} must not exceed
+     *
+     * @return the first (and only) user value in the data; not null.
+     *
+     * @throws NullPointerException if {@code ionData} is null.
+     * @throws UnexpectedEofException if the data doesn't contain any user
+     * values.
+     * @throws IonException if the data does not contain exactly one user
+     * value.
+     */
+    public IonValue singleValue(byte[] ionData, int offset, int len);
+
 
     //-------------------------------------------------------------------------
     // IonReader creation
diff --git a/src/com/amazon/ion/impl/lite/IonSystemLite.java b/src/com/amazon/ion/impl/lite/IonSystemLite.java
index 1e16f739..95bfa4d0 100644
--- a/src/com/amazon/ion/impl/lite/IonSystemLite.java
+++ b/src/com/amazon/ion/impl/lite/IonSystemLite.java
@@ -523,7 +523,12 @@ final class IonSystemLite
 
     public IonValue singleValue(byte[] ionData)
     {
-        IonReader reader = newReader(ionData);
+        return singleValue(ionData, 0, ionData.length);
+    }
+
+    @Override
+    public IonValue singleValue(byte[] ionData, int offset, int len) {
+        IonReader reader = newReader(ionData, offset, len);
         try {
             Iterator<IonValue> it = iterate(reader);
             return singleValue(it);


Test patch:
diff --git a/test/com/amazon/ion/LoaderTest.java b/test/com/amazon/ion/LoaderTest.java
index e00685d2..fc020077 100644
--- a/test/com/amazon/ion/LoaderTest.java
+++ b/test/com/amazon/ion/LoaderTest.java
@@ -32,6 +32,8 @@ import java.io.Reader;
 import java.io.StringReader;
 import java.util.Arrays;
 import java.util.Iterator;
+
+import org.junit.Assert;
 import org.junit.Test;
 
 
@@ -393,6 +395,53 @@ public class LoaderTest
         catch (IonException ie) { /* ok */ }
     }
 
+    @Test
+    public void testSubBufferSingleValue()
+    {
+        IonSystem sys = system();
+
+        String image = "(this is a single sexp)";
+        byte[] bytes =  sys.newLoader().load(image).getBytes();
+
+        IonValue singleValue = sys.singleValue(bytes, 0, bytes.length);
+        assertEquals(singleValue.toString(), image);
+
+        byte[] padded = new byte[bytes.length + 20];
+        System.arraycopy(bytes, 0, padded, 10, bytes.length);
+
+        IonValue paddedValue = sys.singleValue(padded, 10, bytes.length);
+        assertEquals(paddedValue.toString(), image);
+
+        bytes = sys.newLoader().load("(two) (values)").getBytes();
+
+        try {
+            sys.singleValue(bytes, 0, bytes.length);
+            fail("Expected IonException");
+        }
+        catch (IonException ie) {
+            Assert.assertTrue(ie.getMessage().contains("not a single value"));
+        }
+
+        padded = new byte[bytes.length + 20];
+        System.arraycopy(bytes, 0, padded, 10, bytes.length);
+
+        try {
+            sys.singleValue(padded, 10, bytes.length);
+            fail("Expected IonException");
+        }
+        catch (IonException ie) {
+            Assert.assertTrue(ie.getMessage().contains("not a single value"));
+        }
+
+        try {
+            sys.singleValue(padded, 15, bytes.length);
+            fail("Expected IonException");
+        }
+        catch (IonException ie) {
+            Assert.assertTrue(ie.getMessage().contains("bad character"));
+        }
+    }
+
     @Test
     public void testCatalogOnLoader() throws Exception {
         IonSystem sys = newSystem(Symtabs.CATALOG);