in src/main/software/amazon/event/ruler/MachineComplexityEvaluator.java [49:117]
int evaluate(ByteState state) {
// Upfront cost: generate the map of all matches accessible from every state in the machine.
Map<SingleByteTransition, Set<ByteMatch>> matchesAccessibleFromEachTransition =
getMatchesAccessibleFromEachTransition(state);
Set<ByteTransition> visited = new HashSet<>();
visited.add(state);
int maxSize = 0;
// We'll do a breadth-first-search but it shouldn't matter.
Queue<ByteTransition> transitions = new LinkedList<>();
state.getTransitions().forEach(trans -> transitions.add(trans));
while (!transitions.isEmpty()) {
ByteTransition transition = transitions.remove();
if (visited.contains(transition)) {
continue;
}
visited.add(transition);
// The sum of all the wildcard patterns accessible from each SingleByteTransition we are present in on our
// current traversal is the number of wildcard rule prefixes matching a theoretical worst-case input value.
int size = 0;
for (SingleByteTransition single : transition.expand()) {
size += getPatternsWithComplexity(matchesAccessibleFromEachTransition.get(single)).size();
// Look for "transitions for all bytes" (i.e. wildcard transitions). Since an input value that matches
// foo will also match foo*, we also need to include in our size wildcard patterns accessible from foo*.
ByteState nextState = single.getNextByteState();
if (nextState != null) {
for (SingleByteTransition transitionForAllBytes : nextState.getTransitionForAllBytes().expand()) {
if (!(transitionForAllBytes instanceof ByteMachine.EmptyByteTransition) &&
!contains(transition.expand(), transitionForAllBytes)) {
size += getPatternsWithComplexity(matchesAccessibleFromEachTransition.get(transitionForAllBytes))
.size();
}
}
}
}
if (size >= maxComplexity) {
return maxComplexity;
}
if (size > maxSize) {
maxSize = size;
}
// Load up our queue with the next round of transitions, where each transition represents a set of states
// that could be accessed with a particular byte value.
ByteTransition nextTransition = transition.getTransitionForNextByteStates();
if (nextTransition != null) {
nextTransition.getTransitions().forEach(trans -> transitions.add(trans));
}
}
// Now that we have a maxSize for this ByteMachine, let's recursively get the maxSize for each next NameState
// accessible via any of this ByteMachine's matches. We will return the maximum maxSize.
int maxSizeFromNextNameStates = 0;
Set<ByteMatch> uniqueMatches = new HashSet<>();
for (Set<ByteMatch> matches : matchesAccessibleFromEachTransition.values()) {
uniqueMatches.addAll(matches);
}
for (ByteMatch match : uniqueMatches) {
NameState nextNameState = match.getNextNameState();
if (nextNameState != null) {
maxSizeFromNextNameStates = Math.max(maxSizeFromNextNameStates, nextNameState.evaluateComplexity(this));
}
}
return Math.max(maxSize, maxSizeFromNextNameStates);
}