ResourceQuantity.java
package io.github.baokhang83.mnemo.warden.controller.schedule;
import java.math.BigDecimal;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Parses a Kubernetes resource {@code Quantity} string into a byte count, for comparing two
* {@code ResourceProfile} limits to classify a schedule transition's direction (W-304).
*
* <p>Deliberately a small, controller-local copy of {@code warden-agent}'s {@code K8sQuantity},
* not a shared dependency: the two modules are deliberately decoupled (each module's own {@code
* pom.xml} says so), and the agent's copy is tied to a different problem (confirming a PATCHed
* value against the API server's normalized echo) than this one (comparing two static spec
* values) — see W-304's design doc.
*
* <p>Public, not package-private: {@code
* io.github.baokhang83.mnemo.warden.controller.intent.IntentEmitter} (W-306) also needs to parse
* a {@code ResourceProfile}'s {@code request}/{@code limit} to bytes, and reuses this rather than
* a third copy in the same module.
*/
public final class ResourceQuantity {
private static final Map<String, BigDecimal> BINARY_SUFFIXES =
Map.of(
"Ki", BigDecimal.valueOf(1024L),
"Mi", BigDecimal.valueOf(1024L * 1024),
"Gi", BigDecimal.valueOf(1024L * 1024 * 1024),
"Ti", BigDecimal.valueOf(1024L * 1024 * 1024 * 1024));
private static final Map<String, BigDecimal> DECIMAL_SUFFIXES =
Map.of(
"k", BigDecimal.valueOf(1_000L),
"M", BigDecimal.valueOf(1_000_000L),
"G", BigDecimal.valueOf(1_000_000_000L),
"T", BigDecimal.valueOf(1_000_000_000_000L));
private static final Pattern QUANTITY = Pattern.compile("^(\\d+(?:\\.\\d+)?)([A-Za-z]*)$");
private ResourceQuantity() {}
public static long parseBytes(String quantity) {
Matcher matcher = QUANTITY.matcher(quantity.trim());
if (!matcher.matches()) {
throw new IllegalArgumentException("not a recognized Quantity: " + quantity);
}
BigDecimal number = new BigDecimal(matcher.group(1));
String suffix = matcher.group(2);
if (suffix.isEmpty()) {
return number.longValueExact();
}
BigDecimal multiplier = BINARY_SUFFIXES.get(suffix);
if (multiplier == null) {
multiplier = DECIMAL_SUFFIXES.get(suffix);
}
if (multiplier == null) {
throw new IllegalArgumentException("unsupported Quantity suffix: " + suffix + " in " + quantity);
}
return number.multiply(multiplier).longValueExact();
}
}