You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2025/01/05 14:14:44 UTC

(commons-lang) branch master updated: Add ArrayUtils.startsWith()

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new ef3d5d93a Add ArrayUtils.startsWith()
ef3d5d93a is described below

commit ef3d5d93a30ba3934b8f02b88c048451aad8e87a
Author: Gary D. Gregory <ga...@gmail.com>
AuthorDate: Sun Jan 5 09:14:36 2025 -0500

    Add ArrayUtils.startsWith()
---
 src/changes/changes.xml                            |  1 +
 .../java/org/apache/commons/lang3/ArrayUtils.java  | 35 ++++++++++++++++++++++
 .../org/apache/commons/lang3/ArrayUtilsTest.java   | 29 ++++++++++++++++++
 3 files changed, 65 insertions(+)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d268afaa4..463246456 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -87,6 +87,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add SystemUtils.IS_OS_MAC_OSX_SEQUOIA.</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add BasicThreadFactory.builder() and deprecate BasicThreadFactory.Builder().</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add BasicThreadFactory.daemon().</action>
+    <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add ArrayUtils.startsWith.</action>
     <!-- UPDATE -->
     <action                   type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 73 to 78 #1267, #1277, #1283, #1288, #1302.</action>
     <action                   type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">[site] Bump org.codehaus.mojo:taglist-maven-plugin from 3.1.0 to 3.2.1 #1300.</action>
diff --git a/src/main/java/org/apache/commons/lang3/ArrayUtils.java b/src/main/java/org/apache/commons/lang3/ArrayUtils.java
index 4545b18d7..76b856035 100644
--- a/src/main/java/org/apache/commons/lang3/ArrayUtils.java
+++ b/src/main/java/org/apache/commons/lang3/ArrayUtils.java
@@ -7842,6 +7842,41 @@ public class ArrayUtils {
         }
     }
 
+    /**
+     * Tests whether the given data array starts with an expected array, for example, signature bytes.
+     * <p>
+     * If both arrays are null, the method returns true. The method return false when one array is null and the other not.
+     * </p>
+     *
+     * @param data     The data to search, maybe larger than the expected data.
+     * @param expected The expected data to find.
+     * @return whether a match was found.
+     * @since 3.18.0
+     */
+    public static boolean startsWith(final byte[] data, final byte[] expected) {
+        if (data == expected) {
+            return true;
+        }
+        if (data == null || expected == null) {
+            return false;
+        }
+        final int dataLen = data.length;
+        if (expected.length > dataLen) {
+            return false;
+        }
+        if (expected.length == dataLen) {
+            // delegate to Arrays.equals() which has optimizations on Java > 8
+            return Arrays.equals(data, expected);
+        }
+        // Once we are on Java 9+ we can delegate to Arrays here as well (or not).
+        for (int i = 0; i < expected.length; i++) {
+            if (data[i] != expected[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Produces a new {@code boolean} array containing the elements
      * between the start and end indices.
diff --git a/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java b/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java
index aad63f3aa..7256865e7 100644
--- a/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/ArrayUtilsTest.java
@@ -31,6 +31,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.lang.annotation.ElementType;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collections;
@@ -5176,6 +5177,34 @@ public class ArrayUtilsTest extends AbstractLangTest {
         }
     }
 
+    @Test
+    public void testStartsWith() {
+        // edge cases
+        assertTrue(ArrayUtils.startsWith(null, null));
+        assertFalse(ArrayUtils.startsWith(ArrayUtils.EMPTY_BYTE_ARRAY, null));
+        assertFalse(ArrayUtils.startsWith(null, ArrayUtils.EMPTY_BYTE_ARRAY));
+        assertTrue(ArrayUtils.startsWith(ArrayUtils.EMPTY_BYTE_ARRAY, ArrayUtils.EMPTY_BYTE_ARRAY));
+        assertTrue(ArrayUtils.startsWith(new byte[0], new byte[0]));
+        // normal cases
+        assertTrue(ArrayUtils.startsWith(new byte[10], new byte[10]));
+        assertTrue(ArrayUtils.startsWith(new byte[10], new byte[9]));
+        assertTrue(ArrayUtils.startsWith(new byte[10], new byte[1]));
+        final byte[] sig = "Signature".getBytes(StandardCharsets.US_ASCII);
+        final byte[] data = new byte[1024];
+        // data is 0
+        assertFalse(ArrayUtils.startsWith(data, sig));
+        // data is 1 short for expected at the end
+        System.arraycopy(sig, 0, data, 0, sig.length - 1);
+        assertFalse(ArrayUtils.startsWith(data, sig));
+        // data is mimatched at the start
+        System.arraycopy(sig, 0, data, 0, sig.length);
+        data[0] = 0;
+        assertFalse(ArrayUtils.startsWith(data, sig));
+        // data is as expected
+        System.arraycopy(sig, 0, data, 0, sig.length);
+        assertTrue(ArrayUtils.startsWith(data, sig));
+    }
+
     @Test
     public void testSubarrayBoolean() {
         final boolean[] nullArray = null;