aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java83
-rw-r--r--org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java1
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java18
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java27
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java134
6 files changed, 226 insertions, 38 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
index 82c0afec5f..aa7247e105 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/StringUtilsTest.java
@@ -12,6 +12,7 @@ package org.eclipse.jgit.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
@@ -70,4 +71,86 @@ public class StringUtilsTest {
assertEquals("a b c d",
StringUtils.replaceLineBreaksWithSpace("a\r\nb\nc d"));
}
+
+ @Test
+ public void testFormatWithSuffix() {
+ assertEquals("1023", StringUtils.formatWithSuffix(1023));
+ assertEquals("1k", StringUtils.formatWithSuffix(1024));
+ assertEquals("1025", StringUtils.formatWithSuffix(1025));
+ assertEquals("1048575", StringUtils.formatWithSuffix(1024 * 1024 - 1));
+ assertEquals("1m", StringUtils.formatWithSuffix(1024 * 1024));
+ assertEquals("1048577", StringUtils.formatWithSuffix(1024 * 1024 + 1));
+ assertEquals("1073741823",
+ StringUtils.formatWithSuffix(1024 * 1024 * 1024 - 1));
+ assertEquals("1g", StringUtils.formatWithSuffix(1024 * 1024 * 1024));
+ assertEquals("1073741825",
+ StringUtils.formatWithSuffix(1024 * 1024 * 1024 + 1));
+ assertEquals("3k", StringUtils.formatWithSuffix(3 * 1024));
+ assertEquals("3m", StringUtils.formatWithSuffix(3 * 1024 * 1024));
+ assertEquals("2050k",
+ StringUtils.formatWithSuffix(2 * 1024 * 1024 + 2048));
+ assertEquals("3g",
+ StringUtils.formatWithSuffix(3L * 1024 * 1024 * 1024));
+ assertEquals("3000", StringUtils.formatWithSuffix(3000));
+ assertEquals("3000000", StringUtils.formatWithSuffix(3_000_000));
+ assertEquals("1953125k", StringUtils.formatWithSuffix(2_000_000_000));
+ assertEquals("2000000010", StringUtils.formatWithSuffix(2_000_000_010));
+ assertEquals("3000000000",
+ StringUtils.formatWithSuffix(3_000_000_000L));
+ }
+
+ @Test
+ public void testParseWithSuffix() {
+ assertEquals(1024, StringUtils.parseIntWithSuffix("1k", true));
+ assertEquals(1024, StringUtils.parseIntWithSuffix("1 k", true));
+ assertEquals(1024, StringUtils.parseIntWithSuffix("1 k", true));
+ assertEquals(1024, StringUtils.parseIntWithSuffix(" \t1 k \n", true));
+ assertEquals(1024, StringUtils.parseIntWithSuffix("1k", false));
+ assertEquals(1024, StringUtils.parseIntWithSuffix("1K", false));
+ assertEquals(1024 * 1024, StringUtils.parseIntWithSuffix("1m", false));
+ assertEquals(1024 * 1024, StringUtils.parseIntWithSuffix("1M", false));
+ assertEquals(-1024 * 1024,
+ StringUtils.parseIntWithSuffix("-1M", false));
+ assertEquals(1_000_000,
+ StringUtils.parseIntWithSuffix(" 1000000\r\n", false));
+ assertEquals(1024 * 1024 * 1024,
+ StringUtils.parseIntWithSuffix("1g", false));
+ assertEquals(1024 * 1024 * 1024,
+ StringUtils.parseIntWithSuffix("1G", false));
+ assertEquals(3L * 1024 * 1024 * 1024,
+ StringUtils.parseLongWithSuffix("3g", false));
+ assertEquals(3L * 1024 * 1024 * 1024,
+ StringUtils.parseLongWithSuffix("3G", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseIntWithSuffix("2G", false));
+ assertEquals(2L * 1024 * 1024 * 1024,
+ StringUtils.parseLongWithSuffix("2G", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("-1m", true));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("-1000", true));
+ assertThrows(StringIndexOutOfBoundsException.class,
+ () -> StringUtils.parseLongWithSuffix("", false));
+ assertThrows(StringIndexOutOfBoundsException.class,
+ () -> StringUtils.parseLongWithSuffix(" \t \n", false));
+ assertThrows(StringIndexOutOfBoundsException.class,
+ () -> StringUtils.parseLongWithSuffix("k", false));
+ assertThrows(StringIndexOutOfBoundsException.class,
+ () -> StringUtils.parseLongWithSuffix("m", false));
+ assertThrows(StringIndexOutOfBoundsException.class,
+ () -> StringUtils.parseLongWithSuffix("g", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("1T", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("1t", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("Nonumber", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("0x001f", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("beef", false));
+ assertThrows(NumberFormatException.class,
+ () -> StringUtils.parseLongWithSuffix("8000000000000000000G",
+ false));
+ }
}
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
index 74762a9021..ee97c265e9 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties
@@ -793,6 +793,7 @@ uriNotFoundWithMessage={0} not found: {1}
URINotSupported=URI not supported: {0}
userConfigInvalid=Git config in the user's home directory {0} is invalid {1}
validatingGitModules=Validating .gitmodules files
+valueExceedsRange=Value ''{0}'' exceeds the range of {1}
verifySignatureBad=BAD signature from "{0}"
verifySignatureExpired=Expired signature from "{0}"
verifySignatureGood=Good signature from "{0}"
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
index 3d5d0607e7..f7ebe4f40f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java
@@ -821,6 +821,7 @@ public class JGitText extends TranslationBundle {
/***/ public String URINotSupported;
/***/ public String userConfigInvalid;
/***/ public String validatingGitModules;
+ /***/ public String valueExceedsRange;
/***/ public String verifySignatureBad;
/***/ public String verifySignatureExpired;
/***/ public String verifySignatureGood;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
index a369026c97..1ce3e312e2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
@@ -42,6 +42,7 @@ import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.RawParseUtils;
+import org.eclipse.jgit.util.StringUtils;
/**
* Git style {@code .config}, {@code .gitconfig}, {@code .gitmodules} file.
@@ -50,9 +51,6 @@ public class Config {
private static final String[] EMPTY_STRING_ARRAY = {};
- static final long KiB = 1024;
- static final long MiB = 1024 * KiB;
- static final long GiB = 1024 * MiB;
private static final int MAX_DEPTH = 10;
private static final TypedConfigGetter DEFAULT_GETTER = new DefaultTypedConfigGetter();
@@ -765,18 +763,8 @@ public class Config {
*/
public void setLong(final String section, final String subsection,
final String name, final long value) {
- final String s;
-
- if (value >= GiB && (value % GiB) == 0)
- s = String.valueOf(value / GiB) + "g"; //$NON-NLS-1$
- else if (value >= MiB && (value % MiB) == 0)
- s = String.valueOf(value / MiB) + "m"; //$NON-NLS-1$
- else if (value >= KiB && (value % KiB) == 0)
- s = String.valueOf(value / KiB) + "k"; //$NON-NLS-1$
- else
- s = String.valueOf(value);
-
- setString(section, subsection, name, s);
+ setString(section, subsection, name,
+ StringUtils.formatWithSuffix(value));
}
/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
index cc0b995f10..9f96bce251 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/DefaultTypedConfigGetter.java
@@ -126,30 +126,11 @@ public class DefaultTypedConfigGetter implements TypedConfigGetter {
if (str == null) {
return defaultValue;
}
- String n = str.trim();
- if (n.length() == 0) {
- return defaultValue;
- }
- long mul = 1;
- switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) {
- case 'g':
- mul = Config.GiB;
- break;
- case 'm':
- mul = Config.MiB;
- break;
- case 'k':
- mul = Config.KiB;
- break;
- }
- if (mul > 1) {
- n = n.substring(0, n.length() - 1).trim();
- }
- if (n.length() == 0) {
- return defaultValue;
- }
try {
- return mul * Long.parseLong(n);
+ return StringUtils.parseLongWithSuffix(str, false);
+ } catch (StringIndexOutOfBoundsException e) {
+ // Empty
+ return defaultValue;
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(MessageFormat.format(
JGitText.get().invalidIntegerValue, section, name, str),
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
index 61de65cac1..b77fb920ed 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/StringUtils.java
@@ -13,12 +13,20 @@ package org.eclipse.jgit.util;
import java.text.MessageFormat;
import java.util.Collection;
+import org.eclipse.jgit.annotations.NonNull;
import org.eclipse.jgit.internal.JGitText;
/**
* Miscellaneous string comparison utility methods.
*/
public final class StringUtils {
+
+ private static final long KiB = 1024;
+
+ private static final long MiB = 1024 * KiB;
+
+ private static final long GiB = 1024 * MiB;
+
private static final char[] LC;
static {
@@ -307,4 +315,130 @@ public final class StringUtils {
}
return new String(buf, 0, o);
}
+
+ /**
+ * Parses a number with optional case-insensitive suffix 'k', 'm', or 'g'
+ * indicating KiB, MiB, and GiB, respectively. The suffix may follow the
+ * number with optional separation by one or more blanks.
+ *
+ * @param value
+ * {@link String} to parse; with leading and trailing whitespace
+ * ignored
+ * @param positiveOnly
+ * {@code true} to only accept positive numbers, {@code false} to
+ * allow negative numbers, too
+ * @return the value parsed
+ * @throws NumberFormatException
+ * if the {@value} is not parseable, or beyond the range of
+ * {@link Long}
+ * @throws StringIndexOutOfBoundsException
+ * if the string is empty or contains only whitespace, or
+ * contains only the letter 'k', 'm', or 'g'
+ * @since 6.0
+ */
+ public static long parseLongWithSuffix(@NonNull String value,
+ boolean positiveOnly)
+ throws NumberFormatException, StringIndexOutOfBoundsException {
+ String n = value.strip();
+ if (n.isEmpty()) {
+ throw new StringIndexOutOfBoundsException();
+ }
+ long mul = 1;
+ switch (n.charAt(n.length() - 1)) {
+ case 'g':
+ case 'G':
+ mul = GiB;
+ break;
+ case 'm':
+ case 'M':
+ mul = MiB;
+ break;
+ case 'k':
+ case 'K':
+ mul = KiB;
+ break;
+ default:
+ break;
+ }
+ if (mul > 1) {
+ n = n.substring(0, n.length() - 1).trim();
+ }
+ if (n.isEmpty()) {
+ throw new StringIndexOutOfBoundsException();
+ }
+ long number;
+ if (positiveOnly) {
+ number = Long.parseUnsignedLong(n);
+ if (number < 0) {
+ throw new NumberFormatException(
+ MessageFormat.format(JGitText.get().valueExceedsRange,
+ value, Long.class.getSimpleName()));
+ }
+ } else {
+ number = Long.parseLong(n);
+ }
+ if (mul == 1) {
+ return number;
+ }
+ try {
+ return Math.multiplyExact(mul, number);
+ } catch (ArithmeticException e) {
+ throw new NumberFormatException(e.getLocalizedMessage());
+ }
+ }
+
+ /**
+ * Parses a number with optional case-insensitive suffix 'k', 'm', or 'g'
+ * indicating KiB, MiB, and GiB, respectively. The suffix may follow the
+ * number with optional separation by blanks.
+ *
+ * @param value
+ * {@link String} to parse; with leading and trailing whitespace
+ * ignored
+ * @param positiveOnly
+ * {@code true} to only accept positive numbers, {@code false} to
+ * allow negative numbers, too
+ * @return the value parsed
+ * @throws NumberFormatException
+ * if the {@value} is not parseable or beyond the range of
+ * {@link Integer}
+ * @throws StringIndexOutOfBoundsException
+ * if the string is empty or contains only whitespace, or
+ * contains only the letter 'k', 'm', or 'g'
+ * @since 6.0
+ */
+ public static int parseIntWithSuffix(@NonNull String value,
+ boolean positiveOnly)
+ throws NumberFormatException, StringIndexOutOfBoundsException {
+ try {
+ return Math.toIntExact(parseLongWithSuffix(value, positiveOnly));
+ } catch (ArithmeticException e) {
+ throw new NumberFormatException(
+ MessageFormat.format(JGitText.get().valueExceedsRange,
+ value, Integer.class.getSimpleName()));
+ }
+ }
+
+ /**
+ * Formats an integral value as a decimal number with 'k', 'm', or 'g'
+ * suffix if it is an exact multiple of 1024, otherwise returns the value
+ * representation as a decimal number without suffix.
+ *
+ * @param value
+ * Value to format
+ * @return the value's String representation
+ * @since 6.0
+ */
+ public static String formatWithSuffix(long value) {
+ if (value >= GiB && (value % GiB) == 0) {
+ return String.valueOf(value / GiB) + 'g';
+ }
+ if (value >= MiB && (value % MiB) == 0) {
+ return String.valueOf(value / MiB) + 'm';
+ }
+ if (value >= KiB && (value % KiB) == 0) {
+ return String.valueOf(value / KiB) + 'k';
+ }
+ return String.valueOf(value);
+ }
}