docs: private static void validateAddress(
  			final IPAddressStringParameters validationOptions,
  			final MACAddressStringParameters macOptions,
  			final CharSequence str,
  			final int strStartIndex,
  			int strEndIndex,
  			IPAddressParseData ipAddressParseData,
  			MACAddressParseData macAddressParseData,
  			boolean isEmbeddedIPv4) throws AddressStringException {
  		boolean isMac = macAddressParseData != null;
  		AddressParseData parseData;
  		AddressStringFormatParameters stringFormatParams;
  		IPv6AddressStringParameters ipv6SpecificOptions = null;
  		IPv4AddressStringParameters ipv4SpecificOptions = null;
  		MACAddressStringFormatParameters macSpecificOptions = null;
  		AddressStringParameters baseOptions;
  		MACFormat macFormat = null;
  		boolean isBase85;
  		if(isMac) {
  			baseOptions = macOptions;
  			stringFormatParams = macSpecificOptions = macOptions.getFormatParameters();
  			parseData = macAddressParseData.getAddressParseData();
  			isBase85 = false;
  		} else {
  			baseOptions = validationOptions;
  			//later we set stringFormatParams when we know what ip version we have
  			stringFormatParams = null;
  			parseData = ipAddressParseData.getAddressParseData();
  			ipv6SpecificOptions = validationOptions.getIPv6Parameters();
  			isBase85 = ipv6SpecificOptions.allowBase85;
  			ipv4SpecificOptions = validationOptions.getIPv4Parameters();
  		}
  		
  		int index = strStartIndex;
  		
  		//per segment variables
  		int lastSeparatorIndex, digitCount, leadingZeroCount, rangeWildcardIndex, hexDelimiterIndex, singleWildcardCount, wildcardCount;
  		int frontDigitCount, frontLeadingZeroCount, frontWildcardCount, frontSingleWildcardCount, frontHexDelimiterIndex;
  		boolean notOctal, notDecimal, uppercase, isSingleIPv6Hex, isSingleSegment, isDoubleSegment, isStandard;
  		boolean frontNotOctal, frontNotDecimal, frontUppercase, frontIsStandardRange;
  		boolean firstSegmentDashedRange, checkCharCounts;
  		int extendedCharacterIndex, extendedRangeWildcardIndex;
  		boolean atEnd;
  		long currentValueHex, currentFrontValueHex;
  		final int charArray[] = chars;
  		
  		frontDigitCount = frontLeadingZeroCount = frontSingleWildcardCount = digitCount = leadingZeroCount = singleWildcardCount = wildcardCount = frontWildcardCount = 0;
  		extendedCharacterIndex = extendedRangeWildcardIndex = lastSeparatorIndex = rangeWildcardIndex = hexDelimiterIndex = frontHexDelimiterIndex = -1;
  		isStandard = frontIsStandardRange = atEnd = firstSegmentDashedRange = frontNotOctal = frontNotDecimal = frontUppercase = notOctal = notDecimal = uppercase = isSingleIPv6Hex = isSingleSegment = isDoubleSegment = false;
  		currentValueHex = currentFrontValueHex = 0;
  		checkCharCounts = true;
  		
  		boolean endOfHexSegment, isSpace, isZoneChar, isDashedRangeChar, isRangeChar, isJustZero, isSingleWildcard;
  		isSingleWildcard = isJustZero = isSpace = isDashedRangeChar = isRangeChar = isZoneChar = false;
  		long extendedValue = 0;
  		
  		while(index < strEndIndex || (atEnd = (index == strEndIndex))) {
  			char currentChar;
  			if(atEnd) {
  				parseData.setAddressEndIndex(index);
  				int totalDigits = leadingZeroCount + digitCount;
  				IPVersion version = null;
  				boolean isSegmented = isMac ? macFormat != null : (version = ipAddressParseData.getProviderIPVersion()) != null;
  				if(isSegmented) {
  					if(isMac) {
  						currentChar = macFormat.getSeparator();
  						macAddressParseData.setDoubleSegment(isDoubleSegment = (parseData.getSegmentCount() == 1 && currentChar == Address.RANGE_SEPARATOR));
  						if(isDoubleSegment) {
  							macAddressParseData.setExtended(totalDigits == MAC_EXTENDED_DOUBLE_SEGMENT_DIGIT_COUNT);
  						}
  					} else {
  						//current char is either . or : to handle last segment, unless we have double :: in which case we already handled last segment
  						if(version.isIPv4()) {
  							currentChar = IPv4Address.SEGMENT_SEPARATOR;
  						} else { //ipv6
  							if(index == lastSeparatorIndex + 1) {
  								if(index == parseData.getConsecutiveSeparatorIndex() + 2) {
  									//ends with ::, we've already parsed the last segment
  									break;
  								}
  								throw new AddressStringException(str, "ipaddress.error.cannot.end.with.single.separator");
  							} else if(ipAddressParseData.isProvidingMixedIPv6()) {
  								//no need to parse the last segment, since it is mixed we already have
  								break;
  							} else {
  								currentChar = IPv6Address.SEGMENT_SEPARATOR;
  							}
  						}
  					}
  				} else {
  					//no segment separator so far and segmentCount is 0
  					//it could be all addresses like "*", single segment like 12345 , empty "", or prefix only ip address like /64
  					int totalCharacterCount = index - strStartIndex;
  					if(totalCharacterCount == 0) {
  						//it is prefix-only or ""
  						if(!isMac && ipAddressParseData.hasPrefixSeparator()) {
  							if(!validationOptions.allowPrefixOnly) {
  								throw new AddressStringException(str, "ipaddress.error.prefix.only");
  							}
  						} else if(!baseOptions.allowEmpty) {
  							throw new AddressStringException(str, "ipaddress.error.empty");
  						}
  						parseData.setEmpty(true);
  						break;
  					} else if(wildcardCount == totalCharacterCount) {// "*"
  						if(singleWildcardCount > 0 || rangeWildcardIndex >= 0 || leadingZeroCount > 0 || digitCount > 0 || hexDelimiterIndex >= 0) {//wildcards must appear alone
  							throw new AddressStringException(str, index, true);
  						}
  						if(!baseOptions.allowAll) {
  							throw new AddressStringException(str, "ipaddress.error.all");
  						}
  						parseData.setHasWildcard(true);
  						parseData.setAll(true);
  						break;
  					}
  
  					if(isMac) {
  						//we handle the double segment format abcdef-abcdef here
  						int frontTotalDigits = frontLeadingZeroCount + frontDigitCount;
  						if(		(	((totalDigits == MAC_DOUBLE_SEGMENT_DIGIT_COUNT || totalDigits == MAC_EXTENDED_DOUBLE_SEGMENT_DIGIT_COUNT) && (frontTotalDigits == MAC_DOUBLE_SEGMENT_DIGIT_COUNT || frontWildcardCount > 0)) || 
  									(frontTotalDigits == MAC_DOUBLE_SEGMENT_DIGIT_COUNT && wildcardCount > 0) ||
  									(frontWildcardCount > 0 && wildcardCount > 0)
  								) && !firstSegmentDashedRange) {//checks for *-abcdef and abcdef-* and abcdef-abcdef and *-* two segment addresses
  								//firstSegmentDashedRange means that the range character is '|'
  							AddressSize addressSize = macOptions.addressSize;
  							if(addressSize == AddressSize.EUI64 && totalDigits == MAC_DOUBLE_SEGMENT_DIGIT_COUNT) {
  								throw new AddressStringException(str, "ipaddress.error.too.few.segments");
  							} else if(addressSize == AddressSize.MAC && totalDigits == MAC_EXTENDED_DOUBLE_SEGMENT_DIGIT_COUNT) {
  								throw new AddressStringException(str, "ipaddress.error.too.many.segments");
  							}
  
  							//we have aaaaaa-bbbbbb
  							if(!macOptions.allowSingleDashed) {
  								throw new AddressStringException(str, "ipaddress.mac.error.format");
  							}
  							macAddressParseData.setDoubleSegment(isDoubleSegment = true);
  							macAddressParseData.setExtended(totalDigits == MAC_EXTENDED_DOUBLE_SEGMENT_DIGIT_COUNT);
  							currentChar = MACAddress.DASH_SEGMENT_SEPARATOR;
  							checkCharCounts = false; //counted chars already
  						} else if((frontWildcardCount > 0) || (wildcardCount > 0)) {
  							//either x-* or *-x, we treat these as if they can be expanded to x-*-*-*-*-* or *-*-*-*-*-x
  							if(!macOptions.allowSingleDashed) {
  								throw new AddressStringException(str, "ipaddress.mac.error.format");
  							}
  							currentChar = MACAddress.DASH_SEGMENT_SEPARATOR;
  						} else {
  							//a string of digits with no segment separator
  							//here we handle abcdefabcdef or abcdefabcdef|abcdefabcdef or abcdefabcdef-abcdefabcdef
  							if(!baseOptions.allowSingleSegment) {
  								throw new AddressStringException(str, "ipaddress.error.single.segment");
  							}
  							boolean is12Digits = totalDigits == MAC_SINGLE_SEGMENT_DIGIT_COUNT;
  							if(is12Digits || totalDigits == MAC_EXTENDED_SINGLE_SEGMENT_DIGIT_COUNT) {
  								if(rangeWildcardIndex >= 0) {
  									if(frontTotalDigits != (is12Digits ? MAC_SINGLE_SEGMENT_DIGIT_COUNT : MAC_EXTENDED_SINGLE_SEGMENT_DIGIT_COUNT)) {
  										throw new AddressStringException("ipaddress.error.front.digit.count");
  									}
  								}
  								parseData.setSingleSegment(isSingleSegment = true);
  								macAddressParseData.setExtended(!is12Digits);
  								currentChar = MACAddress.COLON_SEGMENT_SEPARATOR;
  								checkCharCounts = false;//counted chars already
  							} else {
  								throw new AddressStringException("ipaddress.error.too.few.segments.digit.count");
  							}
  						}
  					} else {
  						//a string of digits with no segment separator
  						if(!baseOptions.allowSingleSegment) {
  							throw new AddressStringException(str, "ipaddress.error.single.segment");
  						}
  						if(totalDigits == IPV6_SINGLE_SEGMENT_DIGIT_COUNT) {
  							if(rangeWildcardIndex >= 0) {
  								int frontTotalDigits = frontLeadingZeroCount + frontDigitCount;
  								if(frontTotalDigits != IPV6_SINGLE_SEGMENT_DIGIT_COUNT) {
  									throw new AddressStringException("ipaddress.error.front.digit.count");
  								}
  							}
  							parseData.setSingleSegment(isSingleSegment = isSingleIPv6Hex = true);
  							currentChar = IPv6Address.SEGMENT_SEPARATOR;
  							checkCharCounts = false;//counted chars already
  						} else {
  							if(isBase85) {
  								if(extendedRangeWildcardIndex < 0) {
  									if(totalCharacterCount == IPV6_BASE85_SINGLE_SEGMENT_DIGIT_COUNT) {
  										if(!validationOptions.allowIPv6) {
  											throw new AddressStringException(str, "ipaddress.error.ipv6");
  										}
  										ipAddressParseData.setVersion(IPVersion.IPV6);
  										BigInteger val = parseBig85(str, strStartIndex, strEndIndex);
  										long value = val.and(LOW_BITS_MASK).longValue();
  										BigInteger shift64 = val.shiftRight(Long.SIZE);
  										extendedValue = shift64.longValue();
  										//note that even with the correct number of digits, we can have a value too large
  										BigInteger shiftMore = shift64.shiftRight(Long.SIZE);
  										if(!shiftMore.equals(BigInteger.ZERO)) {
  											throw new AddressStringException(str, "ipaddress.error.address.too.large");
  										}
  										parseData.initSegmentData(1);
  										parseData.incrementSegmentCount();
  										assignAttributes(strStartIndex, strEndIndex, parseData, 0, IPv6Address.DEFAULT_TEXTUAL_RADIX, strStartIndex);
  										parseData.setValue(0, 
  												AddressParseData.KEY_LOWER, value,
  												AddressParseData.KEY_UPPER, value,
  												AddressParseData.KEY_EXTENDED_LOWER, extendedValue,
  												AddressParseData.KEY_EXTENDED_UPPER, extendedValue);
  										ipAddressParseData.setBase85(true);
  										break;
  									}
  								} else {
  									if(totalCharacterCount == (IPV6_BASE85_SINGLE_SEGMENT_DIGIT_COUNT << 1) + 1) {/* note that we already check that extendedRangeWildcardIndex is at index 20 */
  										if(!validationOptions.allowIPv6) {
  											throw new AddressStringException(str, "ipaddress.error.ipv6");
  										}
  										ipAddressParseData.setVersion(IPVersion.IPV6);
  										int frontEndIndex = strStartIndex + IPV6_BASE85_SINGLE_SEGMENT_DIGIT_COUNT;
  										BigInteger val = parseBig85(str, strStartIndex, frontEndIndex);
  										BigInteger val2 = parseBig85(str, frontEndIndex + 1, strEndIndex);
  										long value = val.and(LOW_BITS_MASK).longValue();
  										BigInteger shift64 = val.shiftRight(Long.SIZE);
  										extendedValue = shift64.longValue();
  										BigInteger shiftMore = shift64.shiftRight(Long.SIZE);
  										long value2 = val2.and(LOW_BITS_MASK).longValue();
  										shift64 = val2.shiftRight(Long.SIZE);
  										long extendedValue2 = shift64.longValue();
  										shiftMore = shift64.shiftRight(Long.SIZE);
  										if(!shiftMore.equals(BigInteger.ZERO)) {
  											throw new AddressStringException(str, "ipaddress.error.address.too.large");
  										} else if(val.compareTo(val2) > 0) {
  											throw new AddressStringException(str, "ipaddress.error.invalidRange");
  										}
  										parseData.incrementSegmentCount();
  										parseData.initSegmentData(1);
  										assignAttributes(strStartIndex, frontEndIndex, frontEndIndex + 1, strEndIndex, parseData, 0, strStartIndex, frontEndIndex + 1, IPv6Address.DEFAULT_TEXTUAL_RADIX, IPv6Address.DEFAULT_TEXTUAL_RADIX);
  										parseData.setValue(0, 
  												AddressParseData.KEY_LOWER, value,
  												AddressParseData.KEY_UPPER, value2,
  												AddressParseData.KEY_EXTENDED_LOWER, extendedValue,
  												AddressParseData.KEY_EXTENDED_UPPER, extendedValue2);
  										ipAddressParseData.setBase85(true);
  										parseData.setFlag(0, AddressParseData.KEY_RANGE_WILDCARD, true);
  										break;
  									}
  								}
  							}
  							if (digitCount <= IPV4_SINGLE_SEGMENT_OCTAL_DIGIT_COUNT)  {
  								if(rangeWildcardIndex >= 0) {
  									if(frontDigitCount > IPV4_SINGLE_SEGMENT_OCTAL_DIGIT_COUNT) {
  										throw new AddressStringException("ipaddress.error.front.digit.count");
  									}
  								}
  								
  								//we treat as inet_aton, which means decimal ipv4 or if there is a 0 or 0x we treat as octal or hex ipv4
  								currentChar = IPv4Address.SEGMENT_SEPARATOR;
  							} else {
  								throw new AddressStringException("ipaddress.error.too.few.segments.digit.count");
  							}
  						}
  					}
  				}
  			} else {
  				currentChar = str.charAt(index);
  			}
  
  			// evaluate the character
  			if(currentChar <= '9' && currentChar >= '0') {
  				if(currentChar == '0') {
  					if(digitCount > 0) {
  						++digitCount;
  						currentValueHex <<= 4;
  					} else {
  						++leadingZeroCount;
  					}
  				} else {
  					++digitCount;
  					currentValueHex = (currentValueHex << 4) | charArray[currentChar];
  					if(currentChar >= '8') {
  						notOctal = true;
  					}
  				}
  				++index;
  			} else if(currentChar >= 'a' && currentChar <= 'f') {
  				++digitCount;
  				++index;
  				currentValueHex = (currentValueHex << 4) | charArray[currentChar];
  				notOctal = notDecimal = true;
  			} else if(currentChar == IPv4Address.SEGMENT_SEPARATOR) {
  				int segCount = parseData.getSegmentCount();
  				IPVersion version = null;
  				if(!isMac && (version = ipAddressParseData.getProviderIPVersion()) != null && version.isIPv6()) {
  					//we are not base 85, so throw if necessary
  					if(extendedCharacterIndex >= 0) {
  						throw new AddressStringException(str, extendedCharacterIndex);
  					}
  					isBase85 = false;
  					//mixed IPv6 address like 1:2:3:4:5:6:1.2.3.4
  					if(!ipv6SpecificOptions.allowMixed) {
  						throw new AddressStringException(str, "ipaddress.error.no.mixed");
  					}
  					int totalSegmentCount = segCount + IPv6Address.MIXED_REPLACED_SEGMENT_COUNT;
  					if(totalSegmentCount > IPv6Address.SEGMENT_COUNT) {
  						throw new AddressStringException(str, "ipaddress.error.too.many.segments");
  					}
  					if(wildcardCount > 0) {
  						parseData.setHasWildcard(true);
  					}
  					boolean isNotExpandable = wildcardCount > 0 && parseData.getConsecutiveSeparatorIndex() < 0;
  					if(isNotExpandable && 
  							totalSegmentCount < IPv6Address.SEGMENT_COUNT && 
  							ipv6SpecificOptions.allowWildcardedSeparator) {
  						//the '*' is covering an additional ipv6 segment (eg 1:2:3:4:5:*.2.3.4, the * covers both an ipv4 and ipv6 segment)
  						parseData.setFlag(segCount, AddressParseData.KEY_WILDCARD, true);
  						parseData.setValue(segCount, AddressParseData.KEY_UPPER, IPv6Address.MAX_VALUE_PER_SEGMENT);
  						parseData.incrementSegmentCount();
  					}
  					IPAddressStringParameters mixedOptions = ipv6SpecificOptions.getMixedParameters();
  					ParsedIPAddress pa = new ParsedIPAddress(null, str, mixedOptions);
  					validateIPAddress(mixedOptions, str, lastSeparatorIndex + 1, strEndIndex, pa, true);
  					pa.clearQualifier();
  					checkSegments(str, mixedOptions, pa);
  					ipAddressParseData.setMixedParsedAddress(pa);
  					index = pa.getAddressParseData().getAddressEndIndex();
  				} else {
  					//could be mac or ipv4, we handle either one
  					int maxChars;
  					if(isMac) {
  						if(segCount == 0) {
  							if(!macOptions.allowDotted) {
  								throw new AddressStringException(str, "ipaddress.mac.error.format");
  							}
  							macAddressParseData.setFormat(macFormat = MACFormat.DOTTED);
  							parseData.initSegmentData(MACAddress.MEDIA_ACCESS_CONTROL_DOTTED_64_SEGMENT_COUNT);
  						} else {
  							if(macFormat != MACFormat.DOTTED) {
  								throw new AddressStringException(str, "ipaddress.mac.error.mix.format.characters.at.index", index);
  							}
  							if(segCount >= ((macOptions.addressSize == AddressSize.MAC) ? 
  									MACAddress.MEDIA_ACCESS_CONTROL_DOTTED_SEGMENT_COUNT : 
  										MACAddress.MEDIA_ACCESS_CONTROL_DOTTED_64_SEGMENT_COUNT)) {
  								throw new AddressStringException(str, "ipaddress.error.too.many.segments");
  							}
  						}
  						maxChars = 4;//for mac: 1111.2222.3333
  					} else {
  						//we are not base 85, so throw if necessary
  						if(extendedCharacterIndex >= 0) {
  							throw new AddressStringException(str, extendedCharacterIndex);
  						}
  						isBase85 = false;
  						//end of an ipv4 segment
  						if(!validationOptions.allowIPv4) {
  							throw new AddressStringException(str, "ipaddress.error.ipv4");
  						}
  						ipAddressParseData.setVersion(IPVersion.IPV4);
  						stringFormatParams = ipv4SpecificOptions;
  						if(segCount == 0) {
  							parseData.initSegmentData(IPv4Address.SEGMENT_COUNT);
  						} else if(segCount >= IPv4Address.SEGMENT_COUNT) {
  							throw new AddressStringException(str, "ipaddress.error.ipv4.too.many.segments");
  						}
  						maxChars = getMaxIPv4StringLength(3, 8);
  					}
  					if(wildcardCount > 0) {
  						if(!stringFormatParams.rangeOptions.allowsWildcard()) {
  							throw new AddressStringException(str, "ipaddress.error.no.wildcard");
  						}
  						//wildcards must appear alone
  						if(singleWildcardCount > 0 || rangeWildcardIndex >= 0 || leadingZeroCount > 0 || digitCount > 0 || hexDelimiterIndex >= 0) {
  							throw new AddressStringException(str, index, true);
  						}
  						parseData.setHasWildcard(true);
  						parseData.setFlag(segCount, AddressParseData.KEY_WILDCARD, true);
  						parseData.setValue(segCount, AddressParseData.KEY_UPPER, isMac ? MACAddress.MAX_VALUE_PER_DOTTED_SEGMENT : IPv4Address.MAX_VALUE_PER_SEGMENT);
  						int startIndex = index - wildcardCount;
  						assignAttributes(startIndex, index, parseData, segCount, startIndex);
  						wildcardCount = 0;
  					} else {
  						long value;
  						int radix;
  						int startIndex = index - digitCount;
  						int leadingZeroStartIndex = startIndex - leadingZeroCount;
  						if(digitCount == 0) {
  							boolean noLeadingZeros = leadingZeroCount == 0;
  							if(noLeadingZeros && rangeWildcardIndex >= 0 && hexDelimiterIndex < 0) { // we allow an empty range boundary to denote the max value
  								if(isMac) {
  									value = MACAddress.MAX_VALUE_PER_DOTTED_SEGMENT;
  									radix = 16;
  								} else {
  									value = IPv4Address.MAX_VALUE_PER_SEGMENT;
  									radix = 10;
  								}
  							} else if(noLeadingZeros) {
  								// starts with '.', or has two consecutive '.'
  								throw new AddressStringException(str, "ipaddress.error.empty.segment.at.index", index);
  							} else {
  								value = 0;
  								isJustZero = true;
  								startIndex--;
  								if(hexDelimiterIndex >= 0) {
  									if(isMac) {
  										throw new AddressStringException(str, hexDelimiterIndex);
  									}
  									if(!ipv4SpecificOptions.inet_aton_hex) {
  										throw new AddressStringException(str, "ipaddress.error.ipv4.segment.hex");
  									}
  									radix = 16;
  									hexDelimiterIndex = -1;
  								} else if(isMac) {
  									if(leadingZeroCount > 0 && !stringFormatParams.allowLeadingZeros) {
  										throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  									} else if(!stringFormatParams.allowUnlimitedLeadingZeros && leadingZeroCount > maxChars) {
  										throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", leadingZeroStartIndex);
  									} else if(!macSpecificOptions.allowShortSegments && leadingZeroCount < 2) {
  										throw new AddressStringException(str, "ipaddress.error.segment.too.short.at.index", leadingZeroStartIndex);
  									}
  									radix = 16;
  								} else if(leadingZeroCount > 0 && ipv4SpecificOptions.inet_aton_octal) {
  									radix = 8;
  								} else {
  									if(leadingZeroCount > 0) {
  										if(!stringFormatParams.allowLeadingZeros) {
  											throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  										}
  										ipAddressParseData.setHasIPv4LeadingZeros(true);
  									}
  									radix = 10;
  								}
  								leadingZeroCount--;
  								parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_STR, true);
  								assignAttributes(startIndex, index, parseData, segCount, radix, leadingZeroStartIndex);
  							}
  							
  						} else { // digitCount > 0
  							//Note: we cannot do max value check on ipv4 until after all segments have been read due to inet_aton joined segments, 
  							//although we can do a preliminary check here that is in fact needed to prevent overflow when calculating values later
  							if(digitCount > maxChars) {
  								throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", leadingZeroStartIndex);
  							}
  							isSingleWildcard = singleWildcardCount > 0;
  							if(isMac || hexDelimiterIndex >= 0) {
  								if(isMac) {
  									int totalDigits = digitCount + leadingZeroCount;
  									if(hexDelimiterIndex >= 0) {
  										throw new AddressStringException(str, hexDelimiterIndex);
  									} else if(leadingZeroCount > 0 && !stringFormatParams.allowLeadingZeros) {
  										throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  									} else if(!stringFormatParams.allowUnlimitedLeadingZeros && totalDigits > maxChars) {
  										throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", leadingZeroStartIndex);
  									} else if(!macSpecificOptions.allowShortSegments && totalDigits < 2) {
  										throw new AddressStringException(str, "ipaddress.error.segment.too.short.at.index", leadingZeroStartIndex);
  									}
  								} else if(!ipv4SpecificOptions.inet_aton_hex) {
  									throw new AddressStringException(str, "ipaddress.error.ipv4.segment.hex");
  								} else {
  									ipAddressParseData.set_has_inet_aton_value(true);
  								}
  								radix = 16;
  								if(isSingleWildcard) {
  									if(rangeWildcardIndex >= 0) {
  										throw new AddressStringException(str, index, true);
  									}
  									assignSingleWildcard16(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, leadingZeroStartIndex, stringFormatParams);
  									value = 0;
  								} else {
  									value = currentValueHex;
  								}
  								hexDelimiterIndex = -1;
  								notDecimal = false;
  								notOctal = false;
  							} else {
  								boolean isOctal = leadingZeroCount > 0 && ipv4SpecificOptions.inet_aton_octal;
  								if(isOctal) {
  									if(notOctal) {
  										throw new AddressStringException(str, "ipaddress.error.ipv4.invalid.octal.digit");
  									}
  									ipAddressParseData.set_has_inet_aton_value(true);
  									radix = 8;
  									if(isSingleWildcard) {
  										if(rangeWildcardIndex >= 0) {
  											throw new AddressStringException(str, index, true);
  										}
  										switchSingleWildcard8(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, leadingZeroStartIndex, stringFormatParams);
  										value = 0;
  									} else {
  										value = switchValue8(currentValueHex, index - startIndex);
  									}
  								} else {
  									if(notDecimal) {
  										throw new AddressStringException(str, "ipaddress.error.ipv4.invalid.decimal.digit");
  									}
  									if(leadingZeroCount > 0) {
  										if(!stringFormatParams.allowLeadingZeros) {
  											throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  										}
  										ipAddressParseData.setHasIPv4LeadingZeros(true);
  									}
  									radix = 10;
  									if(isSingleWildcard) {
  										if(rangeWildcardIndex >= 0) {
  											throw new AddressStringException(str, index, true);
  										}
  										switchSingleWildcard10(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, leadingZeroStartIndex, ipv4SpecificOptions);
  										value = 0;
  									} else {
  										value = switchValue10(currentValueHex, index - startIndex);
  										isStandard = true;
  									}
  								}
  							}
  							digitCount = 0;
  							currentValueHex = 0;
  						}
  						if(rangeWildcardIndex >= 0) {
  							int frontRadix;
  							long front;
  							int frontStartIndex = rangeWildcardIndex - frontDigitCount, frontEndIndex = rangeWildcardIndex;
  							int frontLeadingZeroStartIndex = frontStartIndex - frontLeadingZeroCount;
  							if(!stringFormatParams.rangeOptions.allowsRangeSeparator()) {
  								throw new AddressStringException(str, "ipaddress.error.no.range");
  							} else if(!stringFormatParams.allowLeadingZeros && frontLeadingZeroCount > 0) {
  								throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  							} else if(frontDigitCount > maxChars) {
  								throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", frontLeadingZeroStartIndex);
  							}
  							boolean frontEmpty = frontStartIndex == frontEndIndex;
  							if(isMac || frontHexDelimiterIndex >= 0) {
  								if(isMac) {
  									if(frontHexDelimiterIndex >= 0) {
  										throw new AddressStringException(str, frontHexDelimiterIndex);
  									} else if(!macSpecificOptions.allowShortSegments && (frontDigitCount + frontLeadingZeroCount) < 2) {
  										throw new AddressStringException(str, "ipaddress.error.segment.too.short.at.index", frontLeadingZeroStartIndex);
  									}
  									if(!frontEmpty) {//we allow the front of a range to be empty in which case it is 0
  										front = currentFrontValueHex;
  									} else {
  										front = 0;
  									}
  								} else if(!ipv4SpecificOptions.inet_aton_hex) {
  									throw new AddressStringException(str, "ipaddress.error.ipv4.segment.hex");
  								} else {
  									ipAddressParseData.set_has_inet_aton_value(true);
  									//Note that here we allow 0x-0x3 or even 0x-3
  									if(!frontEmpty) {//we allow the front of a range to be empty in which case it is 0
  										front = currentFrontValueHex;
  									} else {
  										front = 0;
  									}
  								}
  								frontRadix = 16;
  							} else { 
  								boolean frontIsOctal = frontLeadingZeroCount > 0 && frontHexDelimiterIndex < 0 && ipv4SpecificOptions.inet_aton_octal;
  								if(frontIsOctal) {
  									if(frontNotOctal) {
  										throw new AddressStringException(str, "ipaddress.error.ipv4.invalid.octal.digit");
  									}
  									ipAddressParseData.set_has_inet_aton_value(true);
  									front = switchValue8(currentFrontValueHex, frontEndIndex - frontStartIndex);
  									frontRadix = 8;
  								} else {
  									if(frontNotDecimal) {
  										throw new AddressStringException(str, "ipaddress.error.ipv4.invalid.decimal.digit");
  									}
  									if(frontLeadingZeroCount == 0) {
  										if(!frontEmpty) {
  											parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_STR, true);
  											if(isStandard && leadingZeroCount == 0) {
  												parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_RANGE_STR, true);
  											}
  										}
  									} else {
  										ipAddressParseData.setHasIPv4LeadingZeros(true);
  									}
  									if(!frontEmpty) {//we allow the front of a range to be empty in which case it is 0
  										front = switchValue10(currentFrontValueHex, frontEndIndex - frontStartIndex);
  									} else {
  										front = 0;
  									}
  									frontRadix = 10;
  								}
  							}
  							if(front > value) {
  								throw new AddressStringException(str, "ipaddress.error.invalidRange");
  							} //else we would have to flip the values and the indices and we would not set or flags[ParseData.STANDARD_RANGE_STR_INDEX]
  							if(isJustZero) {
  								isJustZero = false;
  							} else {
  								assignAttributes(frontStartIndex, frontEndIndex, startIndex, index, parseData, segCount, frontLeadingZeroStartIndex, leadingZeroStartIndex, frontRadix, radix);
  								parseData.setValue(segCount, 
  										AddressParseData.KEY_LOWER, front,
  										AddressParseData.KEY_UPPER, value);
  							}
  							parseData.setFlag(segCount, AddressParseData.KEY_RANGE_WILDCARD, true);
  							frontDigitCount = frontLeadingZeroCount = frontWildcardCount = frontSingleWildcardCount = 0;
  							frontNotOctal = frontNotDecimal = frontUppercase = false;
  							frontHexDelimiterIndex = -1;
  							currentFrontValueHex = 0;
  							isStandard = false;
  							isSingleWildcard = false;
  							singleWildcardCount = 0;
  							rangeWildcardIndex = -1;
  						} else if(isJustZero) {
  							isJustZero = false;
  						} else if(isSingleWildcard) {
  							isSingleWildcard = false;
  							singleWildcardCount = 0;
  						} else {
  							if(isStandard) {
  								parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_STR, true);
  								isStandard = false;
  							}
  							assignAttributes(startIndex, index, parseData, segCount, radix, leadingZeroStartIndex);
  							parseData.setValue(segCount, 
  									AddressParseData.KEY_LOWER, value,
  									AddressParseData.KEY_UPPER, value);
  						}
  						leadingZeroCount = 0;
  					}
  					parseData.incrementSegmentCount();
  					lastSeparatorIndex = index;
  					++index;
  				}
  			} else {
  				//this is the case for all IPv6 and MAC segments, as well as the front range of all segments IPv4, IPv6, and MAC
  				//they are in the same case because the range character - is the same as one of the separators - for MAC, 
  				//so further work is required to distinguish between the front of IPv6/IPv4/MAC range and MAC segment
  				//we also handle IPv6 segment and MAC segment in the same place to avoid code duplication
  				if((endOfHexSegment = (currentChar == IPv6Address.SEGMENT_SEPARATOR)) || 
  						(isRangeChar = (currentChar == Address.RANGE_SEPARATOR)) ||
  						(isMac &&
  								(isDashedRangeChar = (currentChar == MACAddress.DASHED_SEGMENT_RANGE_SEPARATOR)) ||
  								(endOfHexSegment = isSpace = (currentChar == MACAddress.SPACE_SEGMENT_SEPARATOR)))) {
  					/*
  					 There are 3 cases here, A, B and C.
  					 A - we have two MAC segments a-b- 
  					 B - we have the front of a range segment, either a-b which is MAC or IPV6,  or a|b or a<space>b which is MAC
  					 C - we have a single segment, either a MAC segment a- or an IPv6 or MAC segment a:
  					 */
  					if(!endOfHexSegment) {
  						/*
  						 Here we have either a '-' or '|' character or a space ' '
  						 
  						 If we have a '-' character:
  						 For MAC address, the cases are:
  						 1. we did not previously set macFormat and we did not previously encounter '|'
  						 		-if rangeWildcardIndex >= 0 we have dashed a-b- we treat as two segments, case A (we cannot have a|b because that would have set macFormat previously)
  						 		-if rangeWildcardIndex < 0, we treat as front of range, case B, later we will know for sure if really front of range
  						 2. we previously set macFormat or we previously encountered '|'
  						 		if set to dashed we treat as one segment, may or may not be range segment, case C
  						 		if we previously encountered '|' we treat as dashed range segment, case C
  						 		if not we treat as front of range, case B
  						 
  						 For IPv6, this is always front of range, case B
  						 
  						 If we have a '|' character, we have front of range, case B
  						*/
  						// we know either isRangeChar or isDashedRangeChar is true at this point
  						if(isMac) {
  							if(macFormat == null) {
  								if(rangeWildcardIndex >= 0 && !firstSegmentDashedRange) {
  									
  									//case A, we have two segments a-b- or a-b| 
  								
  									//we handle the first segment here, we handle the segment segment in the usual place below
  									if(frontHexDelimiterIndex >= 0) {
  										throw new AddressStringException(str, frontHexDelimiterIndex);
  									}
  									if(hexDelimiterIndex >= 0) {
  										throw new AddressStringException(str, hexDelimiterIndex);
  									}
  									if(!macOptions.allowDashed) {
  										throw new AddressStringException(str, "ipaddress.mac.error.format");
  									}
  									macAddressParseData.setFormat(macFormat = MACFormat.DASHED);
  									checkCharCounts = false;//counting chars later
  									parseData.initSegmentData(MACAddress.EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT);
  									if(frontWildcardCount > 0) {
  										if(!stringFormatParams.rangeOptions.allowsWildcard()) {
  											throw new AddressStringException(str, "ipaddress.error.no.wildcard");
  										}
  										if(frontSingleWildcardCount > 0 || frontLeadingZeroCount > 0 || frontDigitCount > 0 || frontHexDelimiterIndex >= 0) {//wildcards must appear alone
  											throw new AddressStringException(str, rangeWildcardIndex, true);
  										}
  										parseData.setHasWildcard(true);
  										parseData.setFlag(0, AddressParseData.KEY_WILDCARD, true);
  										if(isDoubleSegment || digitCount + leadingZeroCount == MAC_DOUBLE_SEGMENT_DIGIT_COUNT) {
  											//even when not already identified as a double segment address, which is something we can see
  											//only when we reach the end of the address, we may have a-b| where a is * and b is a 6 digit value.
  											//Here we are considering the max value of a.
  											//If b is 6 digits, we need to consider the max value of * as if we know already it will be double segment.
  											//We can do this because the max values will be checked after the address has been parsed,
  											//so even if a-b| ends up being a full address a-b|c-d-e-f-a and not a-b|c,
  											//the fact that we have 6 digits here will invalidate the first address,
  											//so we can safely assume that this address must be a double segment a-b|c even before we have seen that.
  											parseData.setValue(0, AddressParseData.KEY_UPPER, MAC_MAX_TRIPLE);
  										} else {
  											parseData.setValue(0, AddressParseData.KEY_UPPER, MACAddress.MAX_VALUE_PER_SEGMENT);
  										}
  										int startIndex = rangeWildcardIndex - frontWildcardCount;
  										assignAttributes(startIndex, rangeWildcardIndex, parseData, 0, startIndex);
  										frontWildcardCount = 0;
  										rangeWildcardIndex = -1;
  									} else {
  										if(!stringFormatParams.allowLeadingZeros && frontLeadingZeroCount > 0) {
  											throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  										}
  										long value = 0;
  										int startIndex = rangeWildcardIndex - frontDigitCount;
  										int leadingZeroStartIndex = startIndex - frontLeadingZeroCount;
  										int endIndex = rangeWildcardIndex;
  										if(frontSingleWildcardCount > 0) {
  											assignSingleWildcard16(currentFrontValueHex, str, startIndex, endIndex, singleWildcardCount, parseData, 0, leadingZeroStartIndex, stringFormatParams);
  										} else {
  											value = currentFrontValueHex;
  											if(!uppercase) {
  												parseData.setFlag(0, AddressParseData.KEY_STANDARD_STR, true);
  											}
  											assignAttributes(startIndex, endIndex, parseData, 0, MACAddress.DEFAULT_TEXTUAL_RADIX, leadingZeroStartIndex);
  											parseData.setValue(0, 
  													AddressParseData.KEY_LOWER, value,
  													AddressParseData.KEY_UPPER, value);
  										}
  										frontDigitCount = frontLeadingZeroCount = frontWildcardCount = frontSingleWildcardCount = 0;
  										frontNotOctal = frontNotDecimal = frontUppercase = false;
  										frontHexDelimiterIndex = rangeWildcardIndex = -1;
  										currentFrontValueHex = 0;
  									}
  									parseData.incrementSegmentCount();
  									//end of handling the first segment a- in a-b-
  									//below we handle b- by setting endOfSegment here
  									endOfHexSegment = isRangeChar;
  								} else {//we will treat this as the front of a range
  									if(isDashedRangeChar) {
  										firstSegmentDashedRange = true;
  									} else {
  										endOfHexSegment = firstSegmentDashedRange;
  									}
  								}
  							} else {
  								if(macFormat == MACFormat.DASHED) {
  									endOfHexSegment = isRangeChar;
  								} else {
  									if(isDashedRangeChar) {
  										throw new AddressStringException(str, index);
  									}
  								}
  							}
  						}
  						isDashedRangeChar = false;//we don't need this var any more, so set it back to default value
  						if(!endOfHexSegment) {
  							if(extendedCharacterIndex < 0) {
  								//case B
  								if(rangeWildcardIndex >= 0) {
  									if(isBase85) {
  										if(extendedCharacterIndex < 0) {
  											extendedCharacterIndex = index;
  										}
  									} else {
  										throw new AddressStringException(str, index, true);
  									}
  								} else {
  									//here is where we handle the front 'a' of a range like 'a-b'
  									rangeWildcardIndex = index;
  									frontIsStandardRange = isRangeChar;
  									frontDigitCount = digitCount;
  									frontLeadingZeroCount = leadingZeroCount;
  									if(frontDigitCount == 0) {
  										if(frontLeadingZeroCount != 0) {
  											frontDigitCount++;
  											frontLeadingZeroCount--;
  										} //else we allow empty front of range to be considered 0
  									}
  									frontNotOctal = notOctal;
  									frontNotDecimal = notDecimal;
  									frontUppercase = uppercase;
  									frontHexDelimiterIndex = hexDelimiterIndex;
  									frontWildcardCount = wildcardCount;
  									frontSingleWildcardCount = singleWildcardCount;
  									currentFrontValueHex = currentValueHex;
  									leadingZeroCount = digitCount = 0;
  									notOctal = notDecimal = uppercase = false;
  									hexDelimiterIndex = -1;
  									wildcardCount = singleWildcardCount = 0;
  									currentValueHex = 0;
  								}
  							}
  							++index;
  						}
  					}
  					//now we know if we are looking at the end of a segment, so we handle that now
  					if(endOfHexSegment) { //either MAC segment a- or a: or 'a ', or IPv6 a:
  						//case C, an ipv6 or mac segment
  						if(hexDelimiterIndex >= 0) {
  							if(!isSingleSegment) {
  								throw new AddressStringException(str, hexDelimiterIndex);
  							}
  							hexDelimiterIndex = -1;
  						}
  						int segCount = parseData.getSegmentCount();
  						int maxChars;
  						if(isMac) {
  							if(segCount == 0) {
  								if(isSingleSegment) {
  									parseData.initSegmentData(1);
  								} else {
  									if(!(isRangeChar ? macOptions.allowDashed : (isSpace ? macOptions.allowSpaceDelimited : macOptions.allowColonDelimited))) {
  										throw new AddressStringException(str, "ipaddress.mac.error.format");
  									}
  									if(isRangeChar) {
  										macAddressParseData.setFormat(macFormat = MACFormat.DASHED);
  										checkCharCounts = false;//counting chars later
  									} else {
  										macAddressParseData.setFormat(macFormat = (isSpace ? MACFormat.SPACE_DELIMITED : MACFormat.COLON_DELIMITED));						
  									}
  									parseData.initSegmentData(MACAddress.EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT);
  								}
  							} else {
  								if(isRangeChar ? (macFormat != MACFormat.DASHED) : (macFormat != (isSpace ? MACFormat.SPACE_DELIMITED : MACFormat.COLON_DELIMITED))) {
  									throw new AddressStringException(str, "ipaddress.mac.error.mix.format.characters.at.index", index);
  								}
  								if(segCount >= ((macOptions.addressSize == AddressSize.MAC) ? 
  										MACAddress.MEDIA_ACCESS_CONTROL_SEGMENT_COUNT : 
  											MACAddress.EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT)) {
  									throw new AddressStringException(str, "ipaddress.error.too.many.segments");
  								}
  							}
  							maxChars = MACAddressSegment.MAX_CHARS;//will be ignored for single or double segments due to countedCharacters and countingCharsLater booleans
  						} else {
  							//we are not base 85, so throw if necessary
  							if(extendedCharacterIndex >= 0) {
  								throw new AddressStringException(str, extendedCharacterIndex);
  							}
  							isBase85 = false;
  							if(segCount == 0) {
  								parseData.initSegmentData(isSingleSegment ? 1 : IPv6Address.SEGMENT_COUNT);
  							} else {
  								if(segCount >= IPv6Address.SEGMENT_COUNT) {
  									throw new AddressStringException(str, "ipaddress.error.too.many.segments");
  								}
  								IPVersion version = ipAddressParseData.getProviderIPVersion();
  								if(version == null || version.isIPv4()) {
  									throw new AddressStringException(str, "ipaddress.error.ipv6.separator");
  								}
  							}
  							if(!validationOptions.allowIPv6) {
  								throw new AddressStringException(str, "ipaddress.error.ipv6");
  							}
  							ipAddressParseData.setVersion(IPVersion.IPV6);
  							stringFormatParams = ipv6SpecificOptions;
  							maxChars = IPv6AddressSegment.MAX_CHARS;//will be ignored for single segment due to countedCharacters and countingCharsLater boolean
  						}
  						if(wildcardCount > 0) {
  							if(!stringFormatParams.rangeOptions.allowsWildcard()) {
  								throw new AddressStringException(str, "ipaddress.error.no.wildcard");
  							}
  							if(singleWildcardCount > 0 || rangeWildcardIndex >= 0 || leadingZeroCount > 0 || digitCount > 0) {//wildcards must appear alone
  								throw new AddressStringException(str, index, true);
  							}
  							parseData.setHasWildcard(true);
  							parseData.setFlag(segCount, AddressParseData.KEY_WILDCARD, true);
  							parseData.setValue(segCount, AddressParseData.KEY_UPPER, isMac ? (isDoubleSegment ? MAC_MAX_TRIPLE : MACAddress.MAX_VALUE_PER_SEGMENT) : IPv6Address.MAX_VALUE_PER_SEGMENT);
  							int startIndex = index - wildcardCount;
  							assignAttributes(startIndex, index, parseData, segCount, startIndex);
  							parseData.incrementSegmentCount();
  							wildcardCount = 0;
  						} else {
  							if(index == strStartIndex) {
  								if(isMac) {
  									throw new AddressStringException(str, "ipaddress.error.empty.segment.at.index", index);
  								}
  								if(index + 1 == strEndIndex) {
  									throw new AddressStringException(str, "ipaddress.error.too.few.segments");
  								}
  								if(str.charAt(index + 1) != IPv6Address.SEGMENT_SEPARATOR) {
  									throw new AddressStringException(str, "ipaddress.error.ipv6.cannot.start.with.single.separator");
  								}
  								//no segment, so we do not increment segmentCount
  							} else if(index == lastSeparatorIndex + 1) {
  								if(isMac) {
  									throw new AddressStringException(str, "ipaddress.error.empty.segment.at.index", index);
  								}
  								if(parseData.getConsecutiveSeparatorIndex() >= 0) {
  									throw new AddressStringException(str, "ipaddress.error.ipv6.ambiguous");
  								}
  								parseData.setConsecutiveSeparatorSegmentIndex(segCount);
  								parseData.setConsecutiveSeparatorIndex(index - 1);
  								assignAttributes(index, index, parseData, segCount, index);
  								parseData.incrementSegmentCount();
  							} else {
  								if(!stringFormatParams.allowLeadingZeros && leadingZeroCount > 0) {
  									throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  								}
  								long value;
  								int startIndex = index - digitCount;
  								int leadingZeroStartIndex = startIndex - leadingZeroCount;
  								if(checkCharCounts && !stringFormatParams.allowUnlimitedLeadingZeros && (digitCount + leadingZeroCount) > maxChars) {
  									throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", leadingZeroStartIndex);
  								} else if(isMac && !macSpecificOptions.allowShortSegments && (digitCount + leadingZeroCount) < 2) {
  									throw new AddressStringException(str, "ipaddress.error.segment.too.short.at.index", leadingZeroStartIndex);
  								}
  								if(digitCount == 0) {
  									if(rangeWildcardIndex >= 0 && leadingZeroCount == 0) {//we allow an empty range boundary to denote the max value
  										value = isMac ? MACAddress.MAX_VALUE_PER_SEGMENT : IPv6Address.MAX_VALUE_PER_SEGMENT;
  									} else {
  										//note we know there is a zero as we have already checked for empty segments so here we know leadingZeroCount is non-zero
  										startIndex--;
  										leadingZeroCount--;
  										isJustZero = true;
  										parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_STR, true);
  										assignAttributes(startIndex, index, parseData, segCount, leadingZeroStartIndex);
  										value = 0;
  									}
  								} else if(checkCharCounts && digitCount > maxChars) {
  									throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", leadingZeroStartIndex);
  								} else { // digitCount > 0
  									if(isSingleWildcard = (singleWildcardCount > 0)) {
  										if(rangeWildcardIndex >= 0) {
  											throw new AddressStringException(str, index, true);
  										}
  										if(isSingleIPv6Hex) {//We need this special call here because single ipv6 hex is 128 bits and cannot fit into a long
  											parseSingleSegmentSingleWildcard16(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, leadingZeroStartIndex, stringFormatParams);
  										} else {
  											assignSingleWildcard16(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, leadingZeroStartIndex, stringFormatParams);
  										}
  										value = 0;
  									} else {
  										if(isSingleIPv6Hex) { //We need this special call here because single ipv6 hex is 128 bits and cannot fit into a long
  											int midIndex = index - 16;
  											if(startIndex < midIndex) {
  												extendedValue = parseLong16(str, startIndex, midIndex);
  												value = parseLong16(str, midIndex, index);
  											} else {
  												value = currentValueHex;
  											}
  										} else {
  											value = currentValueHex;
  										}
  										isStandard = !uppercase;
  									}
  									notOctal = notDecimal = uppercase = false;
  									digitCount = 0;
  									currentValueHex = 0;
  								}
  								if(rangeWildcardIndex >= 0) {
  									int frontStartIndex = rangeWildcardIndex - frontDigitCount, frontEndIndex = rangeWildcardIndex;
  									int frontLeadingZeroStartIndex = frontStartIndex - frontLeadingZeroCount;
  									int frontTotalDigitCount = frontDigitCount + frontLeadingZeroCount;
  									if(!stringFormatParams.rangeOptions.allowsRangeSeparator()) {
  										throw new AddressStringException(str, "ipaddress.error.no.range");
  									} else if(frontHexDelimiterIndex >= 0 && !isSingleSegment) {
  										throw new AddressStringException(str, frontHexDelimiterIndex);
  									} else if(!stringFormatParams.allowLeadingZeros && frontLeadingZeroCount > 0) {
  										throw new AddressStringException(str, "ipaddress.error.segment.leading.zeros");
  									} else if(isMac && !macSpecificOptions.allowShortSegments && frontTotalDigitCount < 2) {
  										throw new AddressStringException(str, "ipaddress.error.segment.too.short.at.index", frontLeadingZeroStartIndex);
  									} else if(checkCharCounts) { 
  										if(frontDigitCount > maxChars) {
  											throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", frontLeadingZeroStartIndex);
  										} else if(!stringFormatParams.allowUnlimitedLeadingZeros && frontTotalDigitCount > maxChars) {
  											throw new AddressStringException(str, "ipaddress.error.segment.too.long.at.index", frontLeadingZeroStartIndex);
  										}
  									}
  									long front, extendedFront;
  									boolean frontEmpty;
  									if(isSingleIPv6Hex) {//We need this special block here because single ipv6 hex is 128 bits and cannot fit into a long
  										frontEmpty = false;
  										int frontMidIndex = frontEndIndex - 16;
  										extendedFront = parseLong16(str, frontStartIndex, frontMidIndex);
  										front = parseLong16(str, frontMidIndex, frontEndIndex);
  									} else {
  										frontEmpty = frontStartIndex == frontEndIndex;
  										if(!frontEmpty) {
  											front = currentFrontValueHex;
  										} else {
  											front = 0;
  										}
  										extendedFront = 0;
  										if(front > value) {
  											throw new AddressStringException(str, "ipaddress.error.invalidRange");
  										} //else we would have to flip the values and the indices and we would not set or flags[ParseData.STANDARD_RANGE_STR_INDEX]
  									}
  									if(isJustZero) {
  										isJustZero = false;
  									} else {
  										if(!frontUppercase && frontLeadingZeroCount == 0 && !frontEmpty) {
  											if(isStandard && leadingZeroCount == 0 && frontIsStandardRange) {
  												parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_STR, true);
  												parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_RANGE_STR, true);
  											} else {
  												parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_STR, true);
  											}
  										}
  										assignAttributes(frontStartIndex, frontEndIndex, startIndex, index, parseData, segCount, frontLeadingZeroStartIndex, leadingZeroStartIndex, IPv6Address.DEFAULT_TEXTUAL_RADIX, IPv6Address.DEFAULT_TEXTUAL_RADIX);
  										if(isSingleIPv6Hex) {//We need this special call here because single ipv6 hex is 128 bits and cannot fit into a long
  											parseData.setValue(segCount, 
  													AddressParseData.KEY_LOWER, front,
  													AddressParseData.KEY_UPPER, value,
  													AddressParseData.KEY_EXTENDED_LOWER, extendedFront,
  													AddressParseData.KEY_EXTENDED_UPPER, extendedValue);
  										} else {
  											parseData.setValue(segCount, 
  													AddressParseData.KEY_LOWER, front,
  													AddressParseData.KEY_UPPER, value);
  										}
  									}
  									parseData.setFlag(segCount, AddressParseData.KEY_RANGE_WILDCARD, true);
  									frontDigitCount = frontLeadingZeroCount = frontWildcardCount = frontSingleWildcardCount = 0;
  									frontNotOctal = frontNotDecimal = frontUppercase = false;
  									frontHexDelimiterIndex = -1;
  									currentFrontValueHex = 0;
  									isStandard = false;
  									isSingleWildcard = false;
  									singleWildcardCount = 0;
  									rangeWildcardIndex = -1;
  								} else if(isJustZero) {
  									isJustZero = false;
  								} else if(isSingleWildcard) {
  									isSingleWildcard = false;
  									singleWildcardCount = 0;
  								} else {
  									if(isStandard) {
  										parseData.setFlag(segCount, AddressParseData.KEY_STANDARD_STR, true);
  										isStandard = false;
  									}
  									assignAttributes(startIndex, index, parseData, segCount, IPv6Address.DEFAULT_TEXTUAL_RADIX /* same as MAC, so no problem */, leadingZeroStartIndex);
  									if(isSingleIPv6Hex) {//We need this special call here because single ipv6 hex is 128 bits and cannot fit into a long
  										parseData.setValue(segCount, 
  												AddressParseData.KEY_LOWER, value,
  												AddressParseData.KEY_UPPER, value,
  												AddressParseData.KEY_EXTENDED_LOWER, extendedValue,
  												AddressParseData.KEY_EXTENDED_UPPER, extendedValue);
  									} else {
  										parseData.setValue(segCount, 
  												AddressParseData.KEY_LOWER, value,
  												AddressParseData.KEY_UPPER, value);	
  									}
  								}
  								parseData.incrementSegmentCount();
  								leadingZeroCount = 0;
  							}
  						}
  						lastSeparatorIndex = index;
  						isSpace = endOfHexSegment = false;
  						++index;
  					}
  					isRangeChar = false;
  				} else if(currentChar >= 'A' && currentChar <= 'F') {
  					++digitCount;
  					++index;
  					currentValueHex = (currentValueHex << 4) | charArray[currentChar];
  					notOctal = notDecimal = uppercase = true;
  				} else if(currentChar == IPAddress.PREFIX_LEN_SEPARATOR) {
  					//we are not base 85, so throw if necessary
  					if(isMac) {
  						throw new AddressStringException(str, index);
  					}
  					strEndIndex = index;
  					ipAddressParseData.setHasPrefixSeparator(true);
  					ipAddressParseData.setQualifierIndex(index + 1);
  				} else if(currentChar == Address.SEGMENT_WILDCARD || (isZoneChar = (currentChar == Address.SEGMENT_SQL_WILDCARD))) {
  					//the character * is always treated as wildcard (but later can be determined to be a base 85 character)
  					
  					//the character % denotes a zone and is also a character for the SQL wildcard,
  					//and it is also a base 85 character,
  					//so we treat it as zone only if the options allow it and it is in the zone position.
  					//Either we have seen an ipv6 segment separator, or we are at the end of the correct number of digits for ipv6 single segment (which rules out base 85 or ipv4 single segment),
  					//or we are the '*' all wildcard so far which can represent everything including ipv6
  					//
  					//In all other cases, the character is treated as wildcard, 
  					//but as is the case of other characters we may later discover we are base 85 ipv6
  					//For base 85, we decided that having the same character mean two different thing depending on position in the string, that is not reasonable.
  					//In fact, if the zone character were allowed, can you tell if there is a zone here or not: %%%%%%%%%%%%%%%%%%%%%%
  					if(isZoneChar && 
  							!isMac && 
  							ipv6SpecificOptions.allowZone &&
  							((parseData.getSegmentCount() > 0 && (isEmbeddedIPv4 || ipAddressParseData.getProviderIPVersion() == IPVersion.IPV6) /* at end of IPv6 regular or mixed */) || 
  									(leadingZeroCount + digitCount == 32 && (rangeWildcardIndex < 0 || frontLeadingZeroCount + frontDigitCount == 32) /* at end of ipv6 single segment */) || 
  									wildcardCount == index /* all wildcards so far */)
  							) {
  						//we are not base 85, so throw if necessary
  						if(extendedCharacterIndex >= 0) {
  							throw new AddressStringException(str, extendedCharacterIndex);
  						}
  						isBase85 = false;
  						strEndIndex = index;
  						ipAddressParseData.setZoned(true);
  						ipAddressParseData.setQualifierIndex(index + 1);
  					} else {
  						++wildcardCount;
  						++index;
  					}
  					isZoneChar = false;
  				} else if(currentChar == Address.SEGMENT_SQL_SINGLE_WILDCARD) {
  					++digitCount;
  					++index;
  					++singleWildcardCount;
  				} else if(currentChar == 'x') {
  					if(digitCount > 0 || leadingZeroCount != 1 || hexDelimiterIndex >= 0 || singleWildcardCount > 0) {
  						if(isBase85) {
  							if(extendedCharacterIndex < 0) {
  								extendedCharacterIndex = index;
  							}
  						} else {
  							throw new AddressStringException(str, index, true);
  						}
  					} else {
  						hexDelimiterIndex = index;
  						leadingZeroCount = 0;
  					}
  					++index;
  				//the remaining possibilities are base85 only
  				} else if(currentChar == IPAddressLargeDivision.EXTENDED_DIGITS_RANGE_SEPARATOR) {
  					if(isBase85) {
  						if(extendedCharacterIndex < 0) {
  							extendedCharacterIndex = index;
  						}
  						int base85TotalDigits = index - strStartIndex;
  						if(base85TotalDigits == IPV6_BASE85_SINGLE_SEGMENT_DIGIT_COUNT) {
  							extendedRangeWildcardIndex = index;
  						} else {
  							throw new AddressStringException(str, extendedCharacterIndex);
  						}
  					} else {
  						throw new AddressStringException(str, index);
  					}
  					++index;
  				} else if(currentChar == IPv6Address.ALTERNATIVE_ZONE_SEPARATOR) {
  					if(isBase85 && !isMac && ipv6SpecificOptions.allowZone) {
  						strEndIndex = index;
  						ipAddressParseData.setZoned(true);
  						ipAddressParseData.setBase85Zoned(true);
  						ipAddressParseData.setQualifierIndex(index + 1);
  					} else {
  						throw new AddressStringException(str, index);
  					}
  				} else {
  					if(isBase85) {
  						if(currentChar < 0 || currentChar > extendedChars.length - 1) {
  							throw new AddressStringException(str, index);
  						}
  						int val = extendedChars[currentChar];
  						if(val == 0) {//note that we already check for the currentChar '0' character at another else/if block, so any other character mapped to the value 0 is an invalid character
  							throw new AddressStringException(str, index);
  						}
  						if(extendedCharacterIndex < 0) {
  							extendedCharacterIndex = index;
  						}
  					} else {
  						throw new AddressStringException(str, index);
  					}
  					++index;
  				}
  			}
  		}
  	}