docs: private static ParsedHostIdentifierStringQualifier parsePortOrService(
  			final CharSequence fullAddr,
  			final CharSequence zone,
  			final HostNameParameters validationOptions,
  			final int index,
  			final int endIndex) throws AddressStringException {
  		boolean isPort = true;
  		boolean hasLetter = false;
  		int digitCount = 0;
  		int charCount = 0;
  		int lastHyphen = -1;
  		boolean isAll = false;
  		for(int i = index; i < endIndex; i++) {
  			char c = fullAddr.charAt(i);
  			if(c >= '1' && c <= '9') {
  				++digitCount;
  				++charCount;
  			} else if(c == '0') {
  				if(digitCount > 0) {
  					++digitCount;
  				}
  				++charCount;
  			} else {
  				//http://www.iana.org/assignments/port-numbers
  				//valid service name chars:
  				//https://tools.ietf.org/html/rfc6335#section-5.1
  				//https://tools.ietf.org/html/rfc6335#section-10.1
  				isPort = false;
  				boolean isHyphen = false;
  				if((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (isHyphen = (c == '-')) || (isAll = (c == Address.SEGMENT_WILDCARD))) {
  					if(isHyphen) {
  						if(i == index) {
  							throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalid.service.hyphen.start");
  						} else if(i - 1 == lastHyphen) {
  							throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalid.service.hyphen.consecutive");
  						} else if(i == endIndex - 1) {
  							throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalid.service.hyphen.end");
  						}
  						lastHyphen = i;
  					} else if(isAll) {
  						if(i > index) {
  							throw new AddressStringException(fullAddr.toString(), i, true);
  						} else if(i + 1 < endIndex) {
  							throw new AddressStringException(fullAddr.toString(), i + 1, true);
  						}
  						hasLetter = true;
  						++charCount;
  						break;
  					} else {
  						hasLetter = true;
  					}
  					++charCount;
  				} else {
  					throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalid.port.service", i);
  				}
  			}
  		}
  		if(isPort) {
  			if(!validationOptions.allowPort) {
  				throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.port");
  			} else if(digitCount == 0) {
  				throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalidPort.no.digits");
  			} else if(digitCount > 5) {
  				throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalidPort.too.large");
  			}
  			int result = parse10(fullAddr, index, endIndex);
  			if(result > 65535) {
  				throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalidPort.too.large");
  			}
  			return new ParsedHostIdentifierStringQualifier(zone, cachePorts(result));
  		} else if(!validationOptions.allowService) {
  			throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.service");
  		} else if(charCount == 0) {
  			throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalidService.no.chars");
  		} else if(charCount > 15) {
  			throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalidService.too.long");
  		} else if(!hasLetter) {
  			throw new AddressStringException(fullAddr.toString(), "ipaddress.host.error.invalidService.no.letter");
  		}
  		CharSequence service = fullAddr.subSequence(index, endIndex);
  		return new ParsedHostIdentifierStringQualifier(zone, service);
  	}