![JAR search and dependency download from the Maven repository](/logo.png)
inet.ipaddr.format.AddressDivisionBase Maven / Gradle / Ivy
Show all versions of ipaddress Show documentation
/*
* Copyright 2016-2018 Sean C Foley
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* or at
* https://github.com/seancfoley/IPAddress/blob/master/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package inet.ipaddr.format;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.TreeMap;
import java.util.function.Supplier;
import inet.ipaddr.AddressSegment;
import inet.ipaddr.IPAddress;
import inet.ipaddr.IPAddressSection.IPStringOptions;
import inet.ipaddr.format.AddressDivisionGroupingBase.AddressStringParams;
import inet.ipaddr.format.AddressDivisionGroupingBase.IPAddressStringParams;
import inet.ipaddr.format.large.IPAddressLargeDivision;
import inet.ipaddr.format.standard.AddressDivisionGrouping.StringOptions.Wildcards;
import inet.ipaddr.format.string.IPAddressStringDivisionSeries;
import inet.ipaddr.format.util.AddressComponentSpliterator;
import inet.ipaddr.format.util.AddressSegmentParams;
/**
* Base class for address divisions.
*
* @author sfoley
*
*/
public abstract class AddressDivisionBase implements AddressGenericDivision {
private static final long serialVersionUID = 4L;
// a set of pre-defined string types
private static final IPStringOptions OCTAL_PARAMS, HEX_PARAMS, DECIMAL_PARAMS;
static {
Wildcards rangeWildcard = new Wildcards(IPAddress.RANGE_SEPARATOR_STR);
OCTAL_PARAMS = new IPStringOptions.Builder(8).setSegmentStrPrefix("0").setWildcards(rangeWildcard).toOptions();
HEX_PARAMS = new IPStringOptions.Builder(16).setSegmentStrPrefix("0x").setWildcards(rangeWildcard).toOptions();
DECIMAL_PARAMS = new IPStringOptions.Builder(10).setWildcards(rangeWildcard).toOptions();
}
private static final String zeros[];
static {
int zerosLength = 20;
zeros = new String[zerosLength];
zeros[0] = "";
for(int i = 1; i < zerosLength; i++) {
zeros[i] = zeros[i - 1] + '0';
}
}
protected static final char[] DIGITS = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z',
};
public static final char[] EXTENDED_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-',
';', '<', '=', '>', '?', '@', '^', '_', '`', '{', '|', '}',
'~' };
protected static final int MIN_RADIX = 2, MAX_RADIX = 85;
protected static final BigInteger BIG_MIN_RADIX = BigInteger.valueOf(MIN_RADIX),
BIG_MAX_RADIX = BigInteger.valueOf(MAX_RADIX);
protected static final char[] UPPERCASE_DIGITS = IPAddressLargeDivision.EXTENDED_DIGITS;
protected static final char[] DOUBLE_DIGITS_DEC = {
'0', '0', '0', '1', '0', '2', '0', '3', '0', '4',
'0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
'1', '0', '1', '1', '1', '2', '1', '3', '1', '4',
'1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
'2', '0', '2', '1', '2', '2', '2', '3', '2', '4',
'2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
'3', '0', '3', '1', '3', '2', '3', '3', '3', '4',
'3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
'4', '0', '4', '1', '4', '2', '4', '3', '4', '4',
'4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
'5', '0', '5', '1', '5', '2', '5', '3', '5', '4',
'5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
'6', '0', '6', '1', '6', '2', '6', '3', '6', '4',
'6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
'7', '0', '7', '1', '7', '2', '7', '3', '7', '4',
'7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
'8', '0', '8', '1', '8', '2', '8', '3', '8', '4',
'8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
'9', '0', '9', '1', '9', '2', '9', '3', '9', '4',
'9', '5', '9', '6', '9', '7', '9', '8', '9', '9',
};
private static TreeMap maxDigitMap = new TreeMap();
private static TreeMap radixPowerMap = new TreeMap();
//cached for performance reasons - especially valuable since segments can be shared amongst different addresses as we do with the masks
protected transient String cachedWildcardString;
/* the cached address bytes */
private transient byte[] lowerBytes, upperBytes;
protected transient int hashCode;
protected AddressDivisionBase() {}
protected boolean isSameValues(AddressDivisionBase other) {
return getValue().equals(other.getValue()) && getUpperValue().equals(other.getUpperValue());
}
/**
* Two divisions are equal if they:
* - they match type/version (ipv4, ipv6, mac, or a specific division class)
* - match bit counts
* - match values
* Prefix lengths, for those divisions that have them, are ignored.
*/
@Override
public boolean equals(Object o) {
if(o == this) {
return true;
}
if(o instanceof AddressDivisionBase) {
// we call isSameValues on the other object to defer to subclasses overriding that method in object o.
// For instance, if we are a grouping class like AddressDivision,
// and if the other is IPv4/6/MAC/AddressSection, then we call the overridden isSameGrouping in the other class,
// which matches type.
// Also, those other classes override equals to ensure flip doesn't go the other way
AddressDivisionBase other = (AddressDivisionBase) o;
return getBitCount() == other.getBitCount() && other.isSameValues(this);
}
return false;
}
protected static int createHashCode(long value, long upperValue) {
return adjustHashCode(1, value, upperValue);
}
static int adjustHashCode(int currentHash, long value, long upperValue) {
long shifted = value >>> 32;
int adjusted = (int) ((shifted == 0) ? value : (value ^ shifted));
currentHash = 31 * currentHash + adjusted;
if(upperValue != value) {
shifted = upperValue >>> 32;
adjusted = (int) ((shifted == 0) ? upperValue : (upperValue ^ shifted));
currentHash = 31 * currentHash + adjusted;
}
return currentHash;
}
@Override
public int hashCode() {
int res = hashCode;
if(res == 0) {
res = 1;
BigInteger lower = getValue(), upper = getUpperValue();
int longBits = Long.SIZE;
do {
long low = lower.longValue();
long up = upper.longValue();
lower = lower.shiftRight(longBits);
upper = upper.shiftRight(longBits);
res = adjustHashCode(res, low, up);
} while(upper.signum() != 0);
hashCode = res;
}
return res;
}
/**
* Gets the bytes for the lowest address in the range represented by this address division.
*
* Since bytes are signed values while addresses are unsigned, values greater than 127 are
* represented as the (negative) two's complement value of the actual value.
* You can get the unsigned integer value i from byte b using i = 0xff & b.
*
* @return
*/
@Override
public byte[] getBytes() {
byte cached[] = lowerBytes;
if(cached == null) {
lowerBytes = cached = getBytesImpl(true);
}
return cached.clone();
}
/**
* Gets the value for the lowest address in the range represented by this address division.
*
* If the value fits in the specified array at the specified index, the same array is returned with the value copied at the specified index.
* Otherwise, a new array is allocated and returned with the value copied at the specified index, and the rest of the array contents the same as the original.
*
* You can use {@link #getBitCount()} to determine the required array length for the bytes.
*
* Since bytes are signed values while addresses are unsigned, values greater than 127 are
* represented as the (negative) two's complement value of the actual value.
* You can get the unsigned integer value i from byte b using i = 0xff & b.
*
* @return
*/
@Override
public byte[] getBytes(byte bytes[], int index) {
byte cached[] = lowerBytes;
if(cached == null) {
lowerBytes = cached = getBytesImpl(true);
}
return getBytes(bytes, index, cached);
}
/**
* Equivalent to {@link #getBytes(byte[], int)} with index of 0.
*/
@Override
public byte[] getBytes(byte bytes[]) {
return getBytes(bytes, 0);
}
private byte[] getBytes(byte[] provided, int startIndex, byte[] cached) {
int byteCount = (getBitCount() + 7) >> 3;
if(provided == null || provided.length < byteCount + startIndex) {
if(startIndex > 0) {
byte bytes2[] = new byte[byteCount + startIndex];
if(provided != null) {
System.arraycopy(provided, 0, bytes2, 0, Math.min(startIndex, provided.length));
}
System.arraycopy(cached, 0, bytes2, startIndex, cached.length);
return bytes2;
}
return cached.clone();
}
System.arraycopy(cached, 0, provided, startIndex, byteCount);
return provided;
}
@Override
public byte[] getUpperBytes() {
if(!isMultiple()) {
return getBytes();
}
byte cached[] = upperBytes;
if(cached == null) {
upperBytes = cached = getBytesImpl(false);
}
return cached.clone();
}
@Override
public byte[] getUpperBytes(byte bytes[], int index) {
if(!isMultiple()) {
return getBytes(bytes, index);
}
byte cached[] = upperBytes;
if(cached == null) {
upperBytes = cached = getBytesImpl(false);
}
return getBytes(bytes, index, cached);
}
@Override
public byte[] getUpperBytes(byte bytes[]) {
return getUpperBytes(bytes, 0);
}
protected abstract byte[] getBytesImpl(boolean low);
/**
* @return the default radix for textual representations of addresses (10 for IPv4, 16 for IPv6)
*/
protected abstract int getDefaultTextualRadix();
/**
* @return the number of digits for the maximum possible value of the division when using the default radix
*/
protected abstract int getMaxDigitCount();
// Returns the maximum number of digits required for the given bit-count and radix.
// The supplied maximum value can be null, in which case it will be calculated if needed,
// otherwise it must correspond to the largest unsigned integer corresponding to the given bit-count.
// So this means you should call this method for bit-counts 63 bits and larger, for which maxValue cannot be stored in a long.
// The bit-count and radix are validated.
// Callers must ensure maxValue is either null, or it is non-negative and corresponds to the given bit-count, otherwise the result will be incorrect.
protected static int getMaxDigitCount(int radix, int bitCount, BigInteger maxValue) {
int result = getDigitCount(radix, bitCount); // validates both radix and bitCount
if(result > 0) {
return result;
}
long key = (((long) radix) << 32) | bitCount;
Integer digs = maxDigitMap.get(key);
if(digs == null) {
if(maxValue == null) {
maxValue = getMaxValue(bitCount);
}
digs = getDigitCount(maxValue, BigInteger.valueOf(radix));
@SuppressWarnings("unchecked")
TreeMap newMaxDigitMap = (TreeMap) maxDigitMap.clone();
newMaxDigitMap.put(key, digs);
maxDigitMap = newMaxDigitMap;
}
return digs;
}
protected static BigInteger getMaxValue(int bitCount) {
if(bitCount < 0) {
throw new IllegalArgumentException();
}
int maxBytes = (bitCount + 7) >>> 3;
byte max[] = new byte[maxBytes];
if(maxBytes > 0) {
int topBits = bitCount % 8;
if(topBits == 0) {
topBits = 8;
}
max[0] = (byte) ~(~0 << topBits);
for(int i = 1; i < max.length; i++) {
max[i] = ~0;
}
}
return new BigInteger(1, max);
}
public static int getDigitCount(BigInteger val, BigInteger radix) {
if(radix.compareTo(BIG_MIN_RADIX) < 0 || radix.compareTo(BIG_MAX_RADIX) > 0) {
throw new IllegalArgumentException();
} else if(val.signum() == 0 || val.equals(BigInteger.ONE)) {
return 1;
}
int result = 1;
while(true) {
val = val.divide(radix);
if(val.signum() == 0) {
break;
}
result++;
}
return result;
}
// Returns the maximum number of digits required for the given bit-count and radix.
// The supplied maximum value must correspond to the largest unsigned integer corresponding to the given bit-count.
// So this means you should avoid calling this method for bit-counts larger than 63.
// The bit-count and radix are validated.
// Callers must ensure maxValue is non-negative and corresponds to the given bit-count, otherwise the result will be incorrect.
protected static int getMaxDigitCount(int radix, int bitCount, long maxValue) {
int result = getDigitCount(radix, bitCount); // validates both radix and bitCount
if(result > 0) {
return result;
}
if(radix == 10) {
if(maxValue < 10) {
return 1;
} else if(maxValue < 100) {
return 2;
} else if(maxValue < 1000) {
return 3;
} else if(maxValue < 10000) {
return 4;
} else if(maxValue < 100000) {
return 5;
} else if(maxValue < 1000000) {
return 6;
} else if(maxValue < 10000000) {
return 7;
} else if(maxValue < 100000000) {
return 8;
} else if(maxValue < 1000000000) {
return 9;
} else if(maxValue < 10000000000L) {
return 10;
} else if(maxValue < 100000000000L) {
return 11;
} else if(maxValue < 1000000000000L) {
return 12;
} else if(maxValue < 10000000000000L) {
return 13;
} else if(maxValue < 100000000000000L) {
return 14;
} else if(maxValue < 1000000000000000L) {
return 15;
} else if(maxValue < 10000000000000000L) {
return 16;
} else if(maxValue < 100000000000000000L) {
return 17;
} else if(maxValue < 1000000000000000000L) {
return 18;
}
return 19;
} else {
long key = (((long) radix) << 32) | bitCount;
Integer digs = maxDigitMap.get(key);
if(digs == null) {
result = 1;
while(true) {
maxValue /= radix;
if(maxValue == 0) {
break;
}
result++;
}
@SuppressWarnings("unchecked")
TreeMap newMaxDigitMap = (TreeMap) maxDigitMap.clone();
newMaxDigitMap.put(key, result);
maxDigitMap = newMaxDigitMap;
} else {
result = digs;
}
return result;
}
}
private static int getDigitCount(int radix, int bitCount) {
if(bitCount <= 0) {
if(bitCount == 0 && radix >= MIN_RADIX && radix <= MAX_RADIX) {
return 1;
}
throw new IllegalArgumentException();
}
switch(radix) {
case 16:
return (bitCount + 3) >> 2; //every 4 bits is another digit
case 8:
return (bitCount + 2) / 3; //every 3 bits is another digit
case 4:
return (bitCount + 1) >> 1; //every 2 bits is another digit
case 2:
return bitCount; //every bit is another digit
case 10:
break;
default:
if(radix < MIN_RADIX || radix > MAX_RADIX) {
throw new IllegalArgumentException();
}
}
return -1;
}
public static int getDigitCount(long value, int radix) {
int result = 1;
if(radix == 16 && value >= 0) {
while(true) {
value >>>= 4;
if(value == 0) {
return result;
}
result++;
}
} else if(radix == 10 && value > -10) {
if(value < 10) {
return 1;
} else if(value < 100) {
return 2;
} else if(value < 1000) {
return 3;
}
value /= 1000;
result = 3; //we start with 3 in the loop below
} else if(radix == 8 && value >= 0) {
while(true) {
value >>>= 3;
if(value == 0) {
break;
}
result++;
}
return result;
} else if(radix == 2 && value > 0) {
return Long.SIZE - Long.numberOfLeadingZeros(value);
} else if(radix < MIN_RADIX || radix > MAX_RADIX) {
throw new IllegalArgumentException();
}
while(true) {
value /= radix;
if(value == 0) {
break;
}
result++;
}
return result;
}
/**
* Caches the results of radix to the given power.
*
* @param radix
* @param power
* @return
*/
protected static BigInteger getRadixPower(BigInteger radix, int power) {
long key = (((long) radix.intValue()) << 32) | power;
BigInteger result = radixPowerMap.get(key);
if(result == null) {
if(radix.compareTo(BIG_MIN_RADIX) < 0 || radix.compareTo(BIG_MAX_RADIX) > 0) {
throw new IllegalArgumentException();
} else if(power == 1) {
result = radix;
} else if((power & 1) == 0) {
BigInteger halfPower = getRadixPower(radix, power >> 1);
result = halfPower.multiply(halfPower);
} else {
BigInteger halfPower = getRadixPower(radix, (power - 1) >> 1);
result = halfPower.multiply(halfPower).multiply(radix);
}
@SuppressWarnings("unchecked")
TreeMap newRadixPowerMap = (TreeMap) radixPowerMap.clone();
newRadixPowerMap.put(key, result);
radixPowerMap = newRadixPowerMap;
}
return result;
}
protected abstract int adjustLowerLeadingZeroCount(int leadingZeroCount, int radix);
protected abstract int adjustUpperLeadingZeroCount(int leadingZeroCount, int radix);
private static void getSplitChar(int count, char splitDigitSeparator, String characters, String stringPrefix, StringBuilder builder) {
while(count-- > 0) {
if(stringPrefix.length() > 0) {
builder.append(stringPrefix);
}
builder.append(characters);
builder.append(splitDigitSeparator);
}
builder.setLength(builder.length() - 1);
}
private static void getSplitChar(int count, char splitDigitSeparator, char character, String stringPrefix, StringBuilder builder) {
int prefLen = stringPrefix.length();
while(count-- > 0) {
if(prefLen > 0) {
builder.append(stringPrefix);
}
builder.append(character);
builder.append(splitDigitSeparator);
}
builder.setLength(builder.length() - 1);
}
private static void getSplitLeadingZeros(int leadingZeroCount, char splitDigitSeparator, String stringPrefix, StringBuilder builder) {
getSplitChar(leadingZeroCount, splitDigitSeparator, '0', stringPrefix, builder);
}
protected static void getLeadingZeros(int leadingZeroCount, StringBuilder builder) {
if(leadingZeroCount > 0) {
String stringArray[] = zeros;
if(leadingZeroCount >= stringArray.length) {
int increment = stringArray.length - 1;
String incrementStr = stringArray[increment];
while(leadingZeroCount >= increment) {
builder.append(incrementStr);
leadingZeroCount -= increment;
}
builder.append(stringArray[leadingZeroCount]);
return;
}
builder.append(stringArray[leadingZeroCount]);
}
}
@Override
public String toString() {
int radix = getDefaultTextualRadix();
IPStringOptions opts;
switch(radix) {
case 8:
opts = OCTAL_PARAMS;
break;
case 16:
opts = HEX_PARAMS;
break;
case 10:
opts = DECIMAL_PARAMS;
break;
default:
opts = new IPStringOptions.Builder(radix).setWildcards(new Wildcards(IPAddress.RANGE_SEPARATOR_STR)).toOptions();
break;
}
StringBuilder builder = new StringBuilder(34);
toParams(opts).appendSingleDivision(this, builder);
return builder.toString();
}
protected static AddressStringParams toParams(IPStringOptions opts) {
//since the params here are not dependent on the section, we could cache the params in the options
//this is not true on the IPv6 side where compression settings change based on the section
@SuppressWarnings("unchecked")
AddressStringParams result = (AddressStringParams) AddressDivisionGroupingBase.getCachedParams(opts);
if(result == null) {
result = new IPAddressStringParams(opts.base, opts.separator, opts.uppercase);
result.expandSegments(opts.expandSegments);
result.setWildcards(opts.wildcards);
result.setSegmentStrPrefix(opts.segmentStrPrefix);
result.setAddressLabel(opts.addrLabel);
result.setReverse(opts.reverse);
result.setSplitDigits(opts.splitDigits);
result.setRadix(opts.base);
result.setUppercase(opts.uppercase);
result.setSeparator(opts.separator);
result.setZoneSeparator(opts.zoneSeparator);
AddressDivisionGroupingBase.setCachedParams(opts, result);
}
return result;
}
/**
* A simple string using just the lower value and the default radix.
*
* @return
*/
protected abstract String getDefaultLowerString();
/**
* A simple string using just the lower and upper values and the default radix, separated by the default range character.
*
* @return
*/
protected abstract String getDefaultRangeString();
/**
* This is the wildcard string to be used when producing the default strings with getString() or getWildcardString()
*
* Since no parameters for the string are provided, default settings are used, but they must be consistent with the address.
*
* For instance, generally the '*' is used as a wildcard to denote all possible values for a given segment,
* but in some cases that character is used for a segment separator.
*
* Note that this only applies to "default" settings, there are additional string methods that allow you to specify these separator characters.
* Those methods must be aware of the defaults as well, to know when they can defer to the defaults and when they cannot.
*
* @return
*/
protected String getDefaultSegmentWildcardString() {
return null;
}
/**
* This is the wildcard string to be used when producing the default strings with getString() or getWildcardString()
*
* Since no parameters for the string are provided, default settings are used, but they must be consistent with the address.
*
* For instance, generally the '-' is used as a range separator, but in some cases that character is used for a segment separator.
*
* Note that this only applies to "default" settings, there are additional string methods that allow you to specify these separator characters.
* Those methods must be aware of the defaults as well, to know when they can defer to the defaults and when they cannot.
*
* @return
*/
protected abstract String getDefaultRangeSeparatorString();
/**
* Produces a normalized string to represent the segment.
* If the segment CIDR prefix length covers the range, then it is assumed to be a CIDR, and the string has only the lower value of the CIDR range.
* Otherwise, the explicit range will be printed.
* @return
*/
protected String getString() {
String result = cachedWildcardString;
if(result == null) {
synchronized(this) {
result = cachedWildcardString;
if(result == null) {
if(!isMultiple()) {
result = getDefaultLowerString();
} else if(!isFullRange() || (result = getDefaultSegmentWildcardString()) == null) {
result = getDefaultRangeString();
}
cachedWildcardString = result;
}
}
}
return result;
}
// this is like a shortcut to getDefaultString() when you already know !isMultiple() or you know isSinglePrefixBlock()
protected String getCachedDefaultLowerString() {
String result = cachedWildcardString;
if(result == null) {
synchronized(this) {
result = cachedWildcardString;
if(result == null) {
cachedWildcardString = result = getDefaultLowerString();
}
}
}
return result;
}
protected String getWildcardString() {
return getString();
}
protected void setDefaultAsFullRangeWildcardString() {
if(cachedWildcardString == null) {
String result = getDefaultSegmentWildcardString();
if(result != null) {
synchronized(this) {
cachedWildcardString = result;
}
}
}
}
protected abstract int getLowerStringLength(int radix);
protected abstract int getUpperStringLength(int radix);
protected abstract void getLowerString(int radix, boolean uppercase, StringBuilder appendable);
protected abstract void getLowerString(int radix, int choppedDigits, boolean uppercase, StringBuilder appendable);
protected abstract void getUpperString(int radix, boolean uppercase, StringBuilder appendable);
protected abstract void getUpperStringMasked(int radix, boolean uppercase, StringBuilder appendable);
protected abstract void getSplitLowerString(int radix, int choppedDigits, boolean uppercase,
char splitDigitSeparator, boolean reverseSplitDigits, String stringPrefix, StringBuilder appendable);
protected abstract void getSplitRangeString(String rangeSeparator, String wildcard, int radix, boolean uppercase,
char splitDigitSeparator, boolean reverseSplitDigits, String stringPrefix, StringBuilder appendable);
protected abstract int getSplitRangeStringLength(String rangeSeparator, String wildcard, int leadingZeroCount, int radix, boolean uppercase,
char splitDigitSeparator, boolean reverseSplitDigits, String stringPrefix);
protected abstract int getRangeDigitCount(int radix);
protected static int toUnsignedStringLength(long value, int radix) {
if(radix < MIN_RADIX || radix > MAX_RADIX) {
throw new IllegalArgumentException();
}
int result;
if(value > 0xffff || (result = toUnsignedStringLengthFast((int) value, radix)) < 0) {
result = toUnsignedStringLengthSlow(value, radix);
}
return result;
}
private static int toUnsignedStringLengthSlow(long value, int radix) {
int count = 1;
boolean useInts = value <= Integer.MAX_VALUE;
int intValue = useInts ? (int) value : radix;
while(intValue >= radix) {
if(useInts) {
intValue /= radix;
} else {
value /= radix;
if(value <= Integer.MAX_VALUE) {
useInts = true;
intValue = (int) value;
}
}
++count;
}
return count;
}
private static int toUnsignedStringLengthFast(int value, int radix) {
if(value <= 1) {//for values larger than 1, result can be different with different radix (radix is 2 and up)
return 1;
}
if(radix == 10) {
//this needs value <= 0xffff (ie 16 bits or less) which is a prereq to calling this method
if(value < 10) {
return 1;
} else if(value < 100) {
return 2;
} else if(value < 1000) {
return 3;
} else if(value < 10000) {
return 4;
}
return 5;
}
if(radix == 16) {
//this needs value <= 0xffff (ie 16 bits or less)
if(value < 0x10) {
return 1;
} else if(value < 0x100) {
return 2;
} else if(value < 0x1000) {
return 3;
}
return 4;
}
if(radix == 8) {
//this needs value <= 0xffff (ie 16 bits or less)
if(value < 010) {
return 1;
} else if(value < 0100) {
return 2;
} else if(value < 01000) {
return 3;
} else if(value < 010000) {
return 4;
} else if(value < 0100000) {
return 5;
}
return 6;
}
if(radix == 2) {
//count the number of digits
//note that we already know value != 0 and that value <= 0xffff
//and we use both of those facts
int digitCount = 15;
int val = value;
if (val >>> 8 == 0) {
digitCount -= 8;
} else {
val >>>= 8;
}
if (val >>> 4 == 0) {
digitCount -= 4;
} else {
val >>>= 4;
}
if (val >>> 2 == 0) {
digitCount -= 2;
} else {
val >>>= 2;
}
//at this point, if (val & 2) != 0 we have undercounted the digit count by 1
if((val & 2) != 0) {
++digitCount;
}
return digitCount;
}
return -1;
}
protected static StringBuilder toUnsignedStringCased(long value, int radix, int choppedDigits, boolean uppercase, StringBuilder appendable) {
if(radix < MIN_RADIX || radix > MAX_RADIX) {
throw new IllegalArgumentException();
} else if(value > 0xffff || !toUnsignedStringFastChopped((int) value, radix, choppedDigits, uppercase, appendable)) {
toUnsignedStringSlow(value, radix, choppedDigits, uppercase, appendable);
}
return appendable;
}
// callers ensure value <= 0xffff
private static boolean toUnsignedStringFastChopped(int value, int radix, int choppedDigits, boolean uppercase, StringBuilder appendable) {
if(toUnsignedStringFast(value, radix, uppercase, appendable)) {
if(choppedDigits > 0) {
appendable.setLength(appendable.length() - choppedDigits);
}
return true;
}
return false;
}
private static char[] getDigits(boolean uppercase, int radix) {
if(uppercase || radix > 36) {
return UPPERCASE_DIGITS;
}
return DIGITS;
}
// callers ensure value <= 0xffff
private static boolean toUnsignedStringFast(int value, int radix, boolean uppercase, StringBuilder appendable) {
if(value <= 1) {//for values larger than 1, result can be different with different radix (radix is 2 and up)
appendable.append(value == 0 ? '0' : '1');
return true;
}
int quotient, remainder; //we iterate on //value == quotient * radix + remainder
if(radix == 10) {
// we know value <= 0xffff (ie 16 bits or less)
if(value < 10) {
char dig[] = getDigits(uppercase, radix);
appendable.append(dig[value]);
return true;
} else if(value < 100) {
char dig[] = DOUBLE_DIGITS_DEC;
int digIndex = value << 1;
appendable.append(dig[digIndex]);
appendable.append(dig[digIndex + 1]);
return true;
} else if(value < 200) {
int index = appendable.length();
appendable.append("127");
if(value != 127) {
char dig[] = DOUBLE_DIGITS_DEC;
index++;
int digIndex = (value - 100) << 1;
appendable.setCharAt(index, dig[digIndex]);
appendable.setCharAt(index + 1, dig[digIndex + 1]);
}
return true;
} else if(value < 300) {
int index = appendable.length();
appendable.append("255");
if(value != 255) {
char dig[] = DOUBLE_DIGITS_DEC;
index++;
int digIndex = (value - 200) << 1;
appendable.setCharAt(index, dig[digIndex]);
appendable.setCharAt(index + 1, dig[digIndex + 1]);
}
return true;
} else if(value < 1000) {
appendable.append(" ");
} else if(value < 10000) {
appendable.append(" ");
} else {
appendable.append(" ");
}
char dig[] = DIGITS;
int index = appendable.length();
do { //value == quotient * 10 + remainder
quotient = (value * 0xcccd) >>> 19; //floor of n/10 is same as floor of ((0xcccd * n / 2^16) / 2^3)
remainder = value - ((quotient << 3) + (quotient << 1)); //multiplication by 2 added to multiplication by 2 ^ 3 is multiplication by 2 + 8 = 10
appendable.setCharAt(--index, dig[remainder]);
value = quotient;
} while(value != 0);
return true;
} else if(radix == 16) {
if(value < 0x10) {
char dig[] = getDigits(uppercase, radix);
appendable.append(dig[value]);
return true;
} else if(value < 0x100) {
appendable.append(" ");
} else if(value < 0x1000) {
appendable.append(" ");
} else {
if(value == 0xffff) {
appendable.append(uppercase ? "FFFF" : "ffff");
return true;
}
appendable.append(" ");
}
char dig[] = getDigits(uppercase, radix);
int index = appendable.length();
do {//value2 == quotient * 16 + remainder
remainder = value & 15;
value >>>= 4;
appendable.setCharAt(--index, dig[remainder]);
} while(value != 0);
return true;
} else if(radix == 8) {
char dig[] = DIGITS;
if(value < 010) {
appendable.append(dig[value]);
return true;
} else if(value < 0100) {
appendable.append(" ");
} else if(value < 01000) {
appendable.append(" ");
} else if(value < 010000) {
appendable.append(" ");
} else if(value < 0100000) {
appendable.append(" ");
} else {
appendable.append(" ");
}
int index = appendable.length();
do {//value2 == quotient * 8 + remainder
remainder = value & 7;
value >>>= 3;
appendable.setCharAt(--index, dig[remainder]);
} while(value != 0);
return true;
} else if(radix == 2) {
//note that we already know value != 0 and that value <= 0xffff
int digitIndex;
if (value >>> 8 == 0) {
if(value == 0xff) {
appendable.append("11111111");
return true;
} else if (value >>> 4 == 0) {
digitIndex = 4;
} else {
digitIndex = 8;
}
} else {
if(value == 0xffff) {
appendable.append("1111111111111111");
return true;
} else if (value >>> 4 == 0) {
digitIndex = 12;
} else {
digitIndex = 16;
}
}
while(--digitIndex > 0) {
int digit = (value >>> digitIndex) & 1;
if(digit == 1) {
appendable.append('1');
while(--digitIndex > 0) {
digit = (value >>> digitIndex) & 1;
appendable.append(digit == 0 ? '0' : '1');
}
break;
}
}
appendable.append((value & 1) == 0 ? '0' : '1');
return true;
}
return false;
}
private static void toUnsignedStringSlow(
long value,
int radix,
int choppedDigits,
boolean uppercase,
StringBuilder appendable) {
int front = appendable.length();
appendDigits(value, radix, choppedDigits, uppercase, appendable);
int back = appendable.length() - 1;
while(front < back) {
char frontChar = appendable.charAt(front);
appendable.setCharAt(front++, appendable.charAt(back));
appendable.setCharAt(back--, frontChar);
}
}
private static void appendDigits(
long value,
int radix,
int choppedDigits,
boolean uppercase,
StringBuilder appendable) {
boolean useInts = value <= Integer.MAX_VALUE;
int value2 = useInts ? (int) value : radix;
int index;
char dig[] = getDigits(uppercase, radix);
while(value2 >= radix) {
if(useInts) {
int val2 = value2;
value2 /= radix;
if(choppedDigits > 0) {
choppedDigits--;
continue;
}
index = val2 % radix;
} else {
long val = value;
value /= radix;
if(value <= Integer.MAX_VALUE) {
useInts = true;
value2 = (int) value;
}
if(choppedDigits > 0) {
choppedDigits--;
continue;
}
index = (int) (val % radix);
}
appendable.append(dig[index]);
}
if(choppedDigits == 0) {
appendable.append(dig[value2]);
}
}
protected void appendUppercase(CharSequence str, int radix, StringBuilder appendable) {
if(radix > 10) {
for(int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if(c >= 'a' && c <= 'z') {
c += 'A' - 'a';
}
appendable.append(c);
}
} else {
appendable.append(str);
}
}
private static int getFullRangeString(String wildcard, StringBuilder appendable) {
if(appendable == null) {
return wildcard.length();
}
appendable.append(wildcard);
return 0;
}
protected int getPrefixAdjustedRangeString(int segmentIndex, AddressSegmentParams params, StringBuilder appendable) {
int leadingZeroCount = params.getLeadingZeros(segmentIndex);
int radix = params.getRadix();
int lowerLeadingZeroCount = adjustLowerLeadingZeroCount(leadingZeroCount, radix);
int upperLeadingZeroCount = adjustUpperLeadingZeroCount(leadingZeroCount, radix);
//if the wildcards match those in use by getString(), and there is no character prefix, let's defer to getString() so that it is cached
Wildcards wildcards = params.getWildcards();
String rangeSeparator = wildcards.rangeSeparator;
int rangeDigitCount = wildcards.singleWildcard == null ? 0 : getRangeDigitCount(radix);
//If we can, we reuse the standard string to construct this string (must have the same radix and no chopped digits)
//We can insert leading zeros, string prefix, and a different separator string if necessary
//Also, we cannot in the case of full range (in which case we are only here because we do not want '*')
if(rangeDigitCount == 0 && radix == getDefaultTextualRadix() && !isFullRange()) {
//we call getString() to cache the result, and we call getString instead of getWildcardString() because it will also mask with the segment prefix length
String str = getString();
String rangeSep = getDefaultRangeSeparatorString();
String stringPrefix = params.getSegmentStrPrefix();
int prefLen = stringPrefix.length();
if(lowerLeadingZeroCount == 0 && upperLeadingZeroCount == 0 && rangeSep.equals(rangeSeparator) && prefLen == 0) {
if(appendable == null) {
return str.length();
} else {
if(params.isUppercase()) {
appendUppercase(str, radix, appendable);
} else {
appendable.append(str);
}
return 0;
}
} else {
if(appendable == null) {
int count = str.length() + (rangeSeparator.length() - rangeSep.length()) +
lowerLeadingZeroCount + upperLeadingZeroCount;
if(prefLen > 0) {
count += prefLen << 1;
}
return count;
} else {
int firstEnd = str.indexOf(rangeSep);
if(prefLen > 0) {
appendable.append(stringPrefix);
}
if(lowerLeadingZeroCount > 0) {
getLeadingZeros(lowerLeadingZeroCount, appendable);
}
appendable.append(str.substring(0, firstEnd));
appendable.append(rangeSeparator);
if(prefLen > 0) {
appendable.append(stringPrefix);
}
if(upperLeadingZeroCount > 0) {
getLeadingZeros(upperLeadingZeroCount, appendable);
}
appendable.append(str.substring(firstEnd + rangeSep.length()));
return 0;
}
}
}
rangeDigitCount = adjustRangeDigits(rangeDigitCount);
if(leadingZeroCount < 0 && appendable == null) {
int charLength = getMaxDigitCount(radix);
String stringPrefix = params.getSegmentStrPrefix();
int prefLen = stringPrefix.length();
if(rangeDigitCount != 0) {
int count = charLength;
if(prefLen > 0) {
count += prefLen;
}
return count;
}
int count = charLength << 1;
if(prefLen > 0) {
count += prefLen << 1;
}
count += rangeSeparator.length();
return count;
}
if(rangeDigitCount != 0) {
return getRangeDigitString(segmentIndex, params, appendable);
}
return getRangeString(segmentIndex, params, lowerLeadingZeroCount, upperLeadingZeroCount, true, appendable);
}
@Override
public int getLowerStandardString(int segmentIndex, AddressSegmentParams params, StringBuilder appendable) {
int count = 0;
String stringPrefix = params.getSegmentStrPrefix();
int prefLen = stringPrefix.length();
if(prefLen > 0) {
if(appendable == null) {
count += prefLen;
} else {
appendable.append(stringPrefix);
}
}
int radix = params.getRadix();
int leadingZeroCount = params.getLeadingZeros(segmentIndex);
if(leadingZeroCount != 0) {
if(appendable == null) {
if(leadingZeroCount < 0) {
return count + getMaxDigitCount(radix);
} else {
count += leadingZeroCount;
}
} else {
leadingZeroCount = adjustLowerLeadingZeroCount(leadingZeroCount, radix);
getLeadingZeros(leadingZeroCount, appendable);
}
}
boolean uppercase = params.isUppercase();
if(radix == getDefaultTextualRadix()) {
String str = getCachedDefaultLowerString();
if(appendable == null) {
return count + str.length();
} else if(uppercase) {
appendUppercase(str, radix, appendable);
} else {
appendable.append(str);
}
} else {
if(appendable == null) {
return count + getLowerStringLength(radix);
} else {
getLowerString(radix, uppercase, appendable);
}
}
return 0;
}
/**
* Produces a string to represent the segment, using wildcards and range characters.
* Use this instead of getWildcardString() if you have a customized wildcard or range separator or you have a non-zero leadingZeroCount,
* or you have a non-standard radix (for IPv4 standard radix is 10, for IPv6 it is 16)
*
*/
@Override
public int getStandardString(int segmentIndex, AddressSegmentParams params, StringBuilder appendable) {
if(!isMultiple()) {
boolean splitDigits = params.isSplitDigits();
if(splitDigits) {
int radix = params.getRadix();
int leadingZeroCount = params.getLeadingZeros(segmentIndex);
leadingZeroCount = adjustLowerLeadingZeroCount(leadingZeroCount, radix);
String stringPrefix = params.getSegmentStrPrefix();
int prefLen = stringPrefix.length();
if(appendable == null) {
int len;
if(leadingZeroCount != 0) {
if(leadingZeroCount < 0) {
len = getMaxDigitCount(radix);
} else {
len = getLowerStringLength(radix) + leadingZeroCount;
}
} else {
len = getLowerStringLength(radix);
}
int count = (len << 1) - 1;
if(prefLen > 0) {
count += len * prefLen;
}
return count;
} else {
char splitDigitSeparator = params.getSplitDigitSeparator() == null ? 0 : params.getSplitDigitSeparator();
boolean reverseSplitDigits = params.isReverseSplitDigits();
boolean uppercase = params.isUppercase();
if(reverseSplitDigits) {
getSplitLowerString(radix, 0, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable);
if(leadingZeroCount != 0) {
appendable.append(splitDigitSeparator);
getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable);
}
} else {
if(leadingZeroCount != 0) {
getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable);
appendable.append(splitDigitSeparator);
}
getSplitLowerString(radix, 0, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable);
}
return 0;
}
}
return getLowerStandardString(segmentIndex, params, appendable);
}
if(isFullRange()) {
String wildcard = params.getWildcards().wildcard;
if(wildcard != null) {
if(wildcard.equals(getDefaultSegmentWildcardString())) {
setDefaultAsFullRangeWildcardString();//cache
}
boolean splitDigits = params.isSplitDigits();
if(splitDigits) {
int radix = params.getRadix();
if(appendable == null) {
int len = getMaxDigitCount(radix);
int count = len * (wildcard.length() + 1) - 1;
return count;
}
char splitDigitSeparator = params.getSplitDigitSeparator() == null ? 0 : params.getSplitDigitSeparator();
getSplitChar(getMaxDigitCount(radix), splitDigitSeparator, wildcard, "", appendable);
return 0;
}
return getFullRangeString(wildcard, appendable);
}
}
return getRangeString(segmentIndex, params, appendable);
}
protected int getRangeString(int segmentIndex, AddressSegmentParams params, StringBuilder appendable) {
boolean splitDigits = params.isSplitDigits();
int radix = params.getRadix();
int leadingZeroCount = params.getLeadingZeros(segmentIndex);
Wildcards wildcards = params.getWildcards();
String rangeSeparator = wildcards.rangeSeparator;
int rangeDigitCount = wildcards.singleWildcard == null ? 0 : getRangeDigitCount(radix);
int lowerLeadingZeroCount = adjustLowerLeadingZeroCount(leadingZeroCount, radix);
int upperLeadingZeroCount = adjustUpperLeadingZeroCount(leadingZeroCount, radix);
//check the case where we can use the result of getWildcardString which is cached.
//It must have same radix and no chopped digits, and no splitting or reversal of digits.
//We can insert leading zeros, string prefix, and a different separator string if necessary.
//Also, we cannot in the case of full range (in which case we are only here because we do not want '*')
if(rangeDigitCount == 0 &&
radix == getDefaultTextualRadix() &&
!splitDigits &&
!isFullRange()) {
String str = getWildcardString();
String rangeSep = getDefaultRangeSeparatorString();
String stringPrefix = params.getSegmentStrPrefix();
int prefLen = stringPrefix.length();
if(lowerLeadingZeroCount == 0 && upperLeadingZeroCount == 0 &&
prefLen == 0 &&
rangeSeparator.equals(rangeSep)) {
if(appendable == null) {
return str.length();
}
appendable.append(str);
return 0;
} else {
if(appendable == null) {
int count = str.length() + (rangeSeparator.length() - rangeSep.length()) + lowerLeadingZeroCount + upperLeadingZeroCount;
if(prefLen > 0) {
count += prefLen << 1;
}
return count;
} else {
int firstEnd = str.indexOf(rangeSep);
if(prefLen > 0) {
appendable.append(stringPrefix);
}
if(lowerLeadingZeroCount > 0) {
getLeadingZeros(lowerLeadingZeroCount, appendable);
}
appendable.append(str.substring(0, firstEnd));
appendable.append(rangeSeparator);
if(prefLen > 0) {
appendable.append(stringPrefix);
}
if(upperLeadingZeroCount > 0) {
getLeadingZeros(upperLeadingZeroCount, appendable);
}
appendable.append(str.substring(firstEnd + rangeSep.length()));
return 0;
}
}
}
/*
split digits that result in digit ranges of * are similar to range digits range digits
eg f00-fff is both f__ and f.*.*
One difference is that for decimal last range digit is 0-5 (ie 255) but for split we only check full range (0-9)
eg 200-255 is 2__ but not 2.*.*
another difference: when calculating range digits, the count is 0 unless the entire range can be written as range digits
eg f10-fff has no range digits but is f.1-f.*
*/
if(!splitDigits && leadingZeroCount < 0 && appendable == null) {
String stringPrefix = params.getSegmentStrPrefix();
int prefLen = stringPrefix.length();
int charLength = getMaxDigitCount(radix);
if(rangeDigitCount != 0) {
int count = charLength;
if(prefLen > 0) {
count += prefLen;
}
return count;
}
int count = charLength << 1;
if(prefLen > 0) {
count += prefLen << 1;
}
count += rangeSeparator.length();
return count;
}
rangeDigitCount = adjustRangeDigits(rangeDigitCount);
if(rangeDigitCount != 0) {
if(splitDigits) {
return getSplitRangeDigitString(segmentIndex, params, appendable);
} else {
return getRangeDigitString(segmentIndex, params, appendable);
}
}
if(splitDigits) {
return getSplitRangeString(segmentIndex, params, appendable);
}
return getRangeString(segmentIndex, params, lowerLeadingZeroCount, upperLeadingZeroCount, false, appendable);
}
protected int getSplitRangeDigitString(
int segmentIndex,
AddressSegmentParams params,
StringBuilder appendable) {
int radix = params.getRadix();
int leadingZerosCount = params.getLeadingZeros(segmentIndex);
leadingZerosCount = adjustLowerLeadingZeroCount(leadingZerosCount, radix);
String stringPrefix = params.getSegmentStrPrefix();
if(appendable == null) {
int len = getLowerStringLength(radix) + leadingZerosCount;
int count = (len << 1) - 1;
int prefLen = stringPrefix.length();
if(prefLen > 0) {
count += len * prefLen;
}
return count;
} else {
Wildcards wildcards = params.getWildcards();
int rangeDigits = adjustRangeDigits(getRangeDigitCount(radix));
char splitDigitSeparator = params.getSplitDigitSeparator() == null ? 0 : params.getSplitDigitSeparator();
boolean reverseSplitDigits = params.isReverseSplitDigits();
boolean uppercase = params.isUppercase();
if(reverseSplitDigits) {
getSplitChar(rangeDigits, splitDigitSeparator, wildcards.singleWildcard, stringPrefix, appendable);
appendable.append(splitDigitSeparator);
getSplitLowerString(radix, rangeDigits, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable);
if(leadingZerosCount > 0) {
appendable.append(splitDigitSeparator);
getSplitLeadingZeros(leadingZerosCount, splitDigitSeparator, stringPrefix, appendable);
}
} else {
if(leadingZerosCount != 0) {
getSplitLeadingZeros(leadingZerosCount, splitDigitSeparator, stringPrefix, appendable);
appendable.append(splitDigitSeparator);
}
getSplitLowerString(radix, rangeDigits, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable);
appendable.append(splitDigitSeparator);
getSplitChar(rangeDigits, splitDigitSeparator, wildcards.singleWildcard, stringPrefix, appendable);
}
}
return 0;
}
protected int getRangeDigitString(
int segmentIndex,
AddressSegmentParams params,
StringBuilder appendable) {
int radix = params.getRadix();
int leadingZerosCount = params.getLeadingZeros(segmentIndex);
leadingZerosCount = adjustLowerLeadingZeroCount(leadingZerosCount, radix);
String stringPrefix = params.getSegmentStrPrefix();
int prefLen = stringPrefix.length();
Wildcards wildcards = params.getWildcards();
int rangeDigits = adjustRangeDigits(getRangeDigitCount(radix));
if(appendable == null) {
return getLowerStringLength(radix) + leadingZerosCount + prefLen;
} else {
if(prefLen > 0) {
appendable.append(stringPrefix);
}
if(leadingZerosCount > 0) {
getLeadingZeros(leadingZerosCount, appendable);
}
boolean uppercase = params.isUppercase();
getLowerString(radix, rangeDigits, uppercase, appendable);
for(int i = 0; i < rangeDigits; i++) {
appendable.append(wildcards.singleWildcard);
}
}
return 0;
}
int adjustRangeDigits(int rangeDigits) {
if(rangeDigits != 0) {
//Note: ranges like ___ intended to represent 0-fff cannot work because the range does not include 2 digit and 1 digit numbers
//This only happens when the lower value is 0 and there is more than 1 range digit
//That's because you can then omit any leading zeros.
//Ranges like f___ representing f000-ffff are fine.
if(!includesZero() || rangeDigits == 1) {
return rangeDigits;
}
}
return 0;
}
protected int getRangeString(
int segmentIndex,
AddressSegmentParams params,
int lowerLeadingZerosCount,
int upperLeadingZerosCount,
boolean maskUpper,
StringBuilder appendable) {
String stringPrefix = params.getSegmentStrPrefix();
int radix = params.getRadix();
String rangeSeparator = params.getWildcards().rangeSeparator;
boolean uppercase = params.isUppercase();
return getRangeString(rangeSeparator, lowerLeadingZerosCount, upperLeadingZerosCount, stringPrefix, radix, uppercase, maskUpper, appendable);
}
protected int getRangeString(
String rangeSeparator,
int lowerLeadingZerosCount,
int upperLeadingZerosCount,
String stringPrefix,
int radix,
boolean uppercase,
boolean maskUpper,
StringBuilder appendable) {
if(radix < MIN_RADIX || radix > MAX_RADIX) {
throw new IllegalArgumentException();
}
int prefLen = stringPrefix.length();
boolean hasStringPrefix = prefLen > 0;
if(appendable == null) {
int count = lowerLeadingZerosCount + upperLeadingZerosCount +
getLowerStringLength(radix) + getUpperStringLength(radix) + rangeSeparator.length();
if(hasStringPrefix) {
count += prefLen << 1;
}
return count;
} else {
if(hasStringPrefix) {
appendable.append(stringPrefix);
}
if(lowerLeadingZerosCount > 0) {
getLeadingZeros(lowerLeadingZerosCount, appendable);
}
getLowerString(radix, uppercase, appendable);
appendable.append(rangeSeparator);
if(hasStringPrefix) {
appendable.append(stringPrefix);
}
if(upperLeadingZerosCount > 0) {
getLeadingZeros(upperLeadingZerosCount, appendable);
}
if(maskUpper) {
getUpperStringMasked(radix, uppercase, appendable);
} else {
getUpperString(radix, uppercase, appendable);
}
}
return 0;
}
protected int getSplitRangeString(
int segmentIndex,
AddressSegmentParams params,
StringBuilder appendable) {
String stringPrefix = params.getSegmentStrPrefix();
int radix = params.getRadix();
int leadingZeroCount = params.getLeadingZeros(segmentIndex);
//for split ranges, it is the leading zeros of the upper value that matters
leadingZeroCount = adjustUpperLeadingZeroCount(leadingZeroCount, radix);
Wildcards wildcards = params.getWildcards();
boolean uppercase = params.isUppercase();
char splitDigitSeparator = params.getSplitDigitSeparator() == null ? 0 : params.getSplitDigitSeparator();
boolean reverseSplitDigits = params.isReverseSplitDigits();
String rangeSeparator = wildcards.rangeSeparator;
if(appendable == null) {
return getSplitRangeStringLength(
rangeSeparator,
wildcards.wildcard,
leadingZeroCount,
radix,
uppercase,
splitDigitSeparator,
reverseSplitDigits,
stringPrefix);
} else {
boolean hasLeadingZeros = leadingZeroCount != 0;
if(hasLeadingZeros && !reverseSplitDigits) {
getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable);
appendable.append(splitDigitSeparator);
hasLeadingZeros = false;
}
getSplitRangeString(
rangeSeparator,
wildcards.wildcard,
radix,
uppercase,
splitDigitSeparator,
reverseSplitDigits,
stringPrefix,
appendable);
if(hasLeadingZeros) {
appendable.append(splitDigitSeparator);
getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable);
}
}
return 0;
}
@FunctionalInterface
protected static interface SegmentCreator {
R applyAsInt(int low, int high);
}
@FunctionalInterface
protected static interface IntBinaryIteratorProvider {
Iterator applyAsInt(boolean isLowest, boolean isHighest, int low, int high);
}
protected static AddressComponentSpliterator createSegmentSpliterator(
T splitForIteration,
int value,
int upperValue,
Supplier> iteratorProvider,
IntBinaryIteratorProvider subIteratorProvider,
SegmentCreator itemProvider) {
return new AddressSegmentSpliterator(splitForIteration, value, upperValue, iteratorProvider, subIteratorProvider, itemProvider);
}
static boolean testRange(BigInteger lowerValue, BigInteger upperValue, BigInteger finalUpperValue, BigInteger networkMask, BigInteger hostMask) {
return lowerValue.equals(lowerValue.and(networkMask)) && finalUpperValue.equals(upperValue.or(hostMask));
}
static boolean testRange(BigInteger lowerValue, BigInteger upperValue, BigInteger finalUpperValue, int bitCount, int divisionPrefixLen) {
BigInteger networkMask = AddressDivisionGroupingBase.ALL_ONES.shiftLeft(bitCount - divisionPrefixLen);
BigInteger hostMask = networkMask.not();
return testRange(lowerValue, upperValue, finalUpperValue, networkMask, hostMask);
}
}