/// <summary> /// Create an instance of the enumerator for a given node. /// </summary> /// <param name="fsa">The automaton to iterate over.</param> /// <param name="node">The starting node's identifier (can be the <see cref="FSA.GetRootNode()"/>.</param> public ByteSequenceEnumerator(FSA fsa, int node) { this.bufferWrapper = ByteBuffer.Wrap(buffer); this.fsa = fsa; if (fsa.GetFirstArc(node) != 0) { RestartFrom(node); } }
// .NET doesn't support Remove() /// <summary> /// Descends to a given node, adds its arcs to the stack to be traversed. /// </summary> private void PushNode(int node) { // Expand buffers if needed. if (position == arcs.Length) { Array.Resize(ref arcs, arcs.Length + ExpectedMaxStates); } arcs[position++] = fsa.GetFirstArc(node); }
/// <summary> /// Calculate perfect hash for a given input sequence of bytes. The perfect hash requires /// that <see cref="FSA"/> is built with <see cref="FSAFlags.Numbers"/> and corresponds to the sequential /// order of input sequences used at automaton construction time. /// </summary> /// <param name="sequence">The byte sequence to calculate perfect hash for.</param> /// <param name="start">Start index in the sequence array.</param> /// <param name="length">Length of the byte sequence, must be at least 1.</param> /// <param name="node">The node to start traversal from, typically the root node (<see cref="FSA.GetRootNode()"/>).</param> /// <returns> /// Returns a unique integer assigned to the input sequence in the automaton (reflecting /// the number of that sequence in the input used to build the automaton). Returns a negative /// integer if the input sequence was not part of the input from which the automaton was created. /// The type of mismatch is a constant defined in <see cref="MatchResult"/>. /// </returns> /// <seealso cref="PerfectHash(byte[])"/> public int PerfectHash(byte[] sequence, int start, int length, int node) { Debug.Assert((fsa.Flags & FSAFlags.Numbers) != 0, $"FSA not built with {FSAFlags.Numbers} option."); Debug.Assert(length > 0, "Must be a non-empty sequence."); int hash = 0; int end = start + length - 1; int seqIndex = start; byte label = sequence[seqIndex]; // Seek through the current node's labels, looking for 'label', update hash. for (int arc = fsa.GetFirstArc(node); arc != 0;) { if (fsa.GetArcLabel(arc) == label) { if (fsa.IsArcFinal(arc)) { if (seqIndex == end) { return(hash); } hash++; } if (fsa.IsArcTerminal(arc)) { /* The automaton contains a prefix of the input sequence. */ return(MatchResult.AutomatonHasPrefix); } // The sequence is a prefix of one of the sequences stored in the automaton. if (seqIndex == end) { return(MatchResult.SequenceIsAPrefix); } // Make a transition along the arc, go the target node's first arc. arc = fsa.GetFirstArc(fsa.GetEndNode(arc)); label = sequence[++seqIndex]; continue; } else { if (fsa.IsArcFinal(arc)) { hash++; } if (!fsa.IsArcTerminal(arc)) { hash += fsa.GetRightLanguageCount(fsa.GetEndNode(arc)); } } arc = fsa.GetNextArc(arc); } if (seqIndex > start) { return(MatchResult.AutomatonHasPrefix); } else { // Labels of this node ended without a match on the sequence. // Perfect hash does not exist. return(MatchResult.NoMatch); } }