in src/main/software/amazon/event/ruler/ByteMachine.java [461:570]
private void doTransitionOn(final String valString, final Set<NameStateWithPattern> transitionTo,
TransitionValueType valueType) {
final Map<NameState, List<Patterns>> failedAnythingButs = new HashMap<>();
final byte[] val = valString.getBytes(StandardCharsets.UTF_8);
// we need to add the name state for key existence
addExistenceMatch(transitionTo);
// attempt to harvest the possible suffix match
addSuffixMatch(val, transitionTo, failedAnythingButs);
if (startStateMatch != null) {
transitionTo.add(new NameStateWithPattern(startStateMatch.getNextNameState(), startStateMatch.getPattern()));
}
// we have to do old-school indexing rather than "for (byte b : val)" because there is some special-casing
// on transitions on the last byte in the value array
ByteTransition trans = startState;
for (int valIndex = 0; valIndex < val.length; valIndex++) {
final ByteTransition nextTrans = getTransition(trans, val[valIndex]);
attemptAddShortcutTransitionMatch(nextTrans, valString, EXACT, transitionTo);
if (!nextTrans.isShortcutTrans()) {
// process any matches hanging off this transition
for (ByteMatch match : nextTrans.getMatches()) {
switch (match.getPattern().type()) {
case EXACT:
case EQUALS_IGNORE_CASE:
case WILDCARD:
if (valIndex == (val.length - 1)) {
transitionTo.add(new NameStateWithPattern(match.getNextNameState(), match.getPattern()));
}
break;
case NUMERIC_EQ:
// only matches at last character
if (valueType == TransitionValueType.NUMERIC && valIndex == (val.length - 1)) {
transitionTo.add(new NameStateWithPattern(match.getNextNameState(), match.getPattern()));
}
break;
case PREFIX:
case PREFIX_EQUALS_IGNORE_CASE:
transitionTo.add(new NameStateWithPattern(match.getNextNameState(), match.getPattern()));
break;
case ANYTHING_BUT_SUFFIX:
case SUFFIX:
case SUFFIX_EQUALS_IGNORE_CASE:
case EXISTS:
// we already harvested these matches via separate functions due to special matching
// requirements, so just ignore them here.
break;
case NUMERIC_RANGE:
// as soon as you see the match, you've matched
Range range = (Range) match.getPattern();
if ((valueType == TransitionValueType.NUMERIC && !range.isCIDR) ||
(valueType != TransitionValueType.NUMERIC && range.isCIDR)) {
transitionTo.add(new NameStateWithPattern(match.getNextNameState(), match.getPattern()));
}
break;
case ANYTHING_BUT:
AnythingBut anythingBut = (AnythingBut) match.getPattern();
// only applies if at last character
if (valIndex == (val.length - 1) &&
anythingBut.isNumeric() == (valueType == TransitionValueType.NUMERIC)) {
addToAnythingButsMap(failedAnythingButs, match.getNextNameState(), match.getPattern());
}
break;
case ANYTHING_BUT_IGNORE_CASE:
case ANYTHING_BUT_WILDCARD:
// only applies if at last character
if (valIndex == (val.length - 1)) {
addToAnythingButsMap(failedAnythingButs, match.getNextNameState(), match.getPattern());
}
break;
case ANYTHING_BUT_PREFIX:
addToAnythingButsMap(failedAnythingButs, match.getNextNameState(), match.getPattern());
break;
default:
throw new RuntimeException("Not implemented yet");
}
}
}
trans = nextTrans.getTransitionForNextByteStates();
if (trans == null) {
break;
}
}
// This may look like premature optimization, but the first "if" here yields roughly 10x performance
// improvement. We exclude CIDR because the value will have been transformed, causing the anythingBut to always
// match. Wait for NUMERIC or STRING matching to harvest anythingBut matches.
if (!anythingButs.isEmpty() && valueType != TransitionValueType.CIDR) {
for (Map.Entry<NameState, List<Patterns>> entry : anythingButs.entrySet()) {
boolean failedAnythingButsContainsKey = failedAnythingButs.containsKey(entry.getKey());
for (Patterns pattern : entry.getValue()) {
if (!failedAnythingButsContainsKey ||
!failedAnythingButs.get(entry.getKey()).contains(pattern)) {
transitionTo.add(new NameStateWithPattern(entry.getKey(), pattern));
}
}
}
}
}