All Downloads are FREE. Search and download functionalities are using the official Maven repository.

inet.ipaddr.format.AddressDivisionBase Maven / Gradle / Ivy

There is a newer version: 5.5.1
Show newest version
/*
 * 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); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy