/// <summary> /// Build DFAs from a provided NFA /// /// This method is used when you want to build the NFA yourself instead of letting this class do it. /// /// Languages built simultaneously will be globally minimized and will share as many states as possible. /// </summary> /// <param name="nfa">The NFA</param> /// <param name="nfaStartStates">The return value will include the DFA states corresponding to these NFA states, in the same order</param> /// <param name="ambiguityResolver">When patterns for multiple results match the same string, this is called to /// combine the multiple results into one. If this is null, then a DfaAmbiguityException will be thrown in that case.</param> /// <param name="cache">If this cache is non-null, it will be checked for a memoized result for this NFA, and will be populated /// with a memoized result when the call is complete.</param> /// <returns>DFA start states that are equivalent to the given NFA start states. This will have the same length as nfaStartStates, with /// corresponding start states in corresponding positions.</returns> public static IList <DfaState <TResult> > BuildFromNfa(Nfa <TResult> nfa, int[] nfaStartStates, DfaAmbiguityResolver <TResult> ambiguityResolver, IBuilderCache cache) { string cacheKey = null; SerializableDfa <TResult> serializableDfa = null; if (cache != null) { var hashAlg = new SHA256Managed(); using (var ms = new MemoryStream()) { using var cs = new CryptoStream(ms, hashAlg, CryptoStreamMode.Write); var bf = new BinaryFormatter(); bf.Serialize(ms, nfaStartStates); bf.Serialize(ms, nfa); bf.Serialize(ms, ambiguityResolver); ms.Flush(); cs.FlushFinalBlock(); cacheKey = Base32.GetDigest(hashAlg.Hash); } serializableDfa = (SerializableDfa <TResult>)cache.GetCachedItem(cacheKey); } if (serializableDfa == null) { var rawDfa = new DfaFromNfa <TResult>(nfa, nfaStartStates, ambiguityResolver).GetDfa(); var minimalDfa = new DfaMinimizer <TResult>(rawDfa).GetMinimizedDfa(); serializableDfa = new SerializableDfa <TResult>(minimalDfa); if (cacheKey != null) { cache.MaybeCacheItem(cacheKey, serializableDfa); } } return(serializableDfa.GetStartStates()); }
private string GetCacheKey(int dfaType, IList <ISet <TResult> > languages, DfaAmbiguityResolver <TResult> ambiguityResolver) { string cacheKey; var hashAlg = new SHA256Managed(); using (var ms = new MemoryStream()) { using var cs = new CryptoStream(ms, hashAlg, CryptoStreamMode.Write); var bf = new BinaryFormatter(); bf.Serialize(ms, dfaType); var numLangs = languages.Count; bf.Serialize(ms, numLangs); //write key stuff out in an order based on our LinkedHashMap, for deterministic serialization foreach (var patEntry in patterns) { var included = false; var patList = patEntry.Value; if (patList.Count == 0) { continue; } for (var i = 0; i < numLangs; ++i) { if (!languages[i].Contains(patEntry.Key)) { continue; } included = true; break; } if (!included) { continue; } bf.Serialize(ms, patList.Count); if (numLangs > 1) { var bits = languages[0].Contains(patEntry.Key) ? 1 : 0; for (var i = 1; i < languages.Count; ++i) { if ((i & 31) == 0) { bf.Serialize(ms, bits); bits = 0; } if (languages[i].Contains(patEntry.Key)) { bits |= 1 << (i & 31); } } bf.Serialize(ms, bits); } foreach (var pat in patList) { bf.Serialize(ms, pat); } bf.Serialize(ms, patEntry.Key); } bf.Serialize(ms, 0); //0-size pattern list terminates pattern map bf.Serialize(ms, ambiguityResolver ?? (object)0); ms.Flush(); cs.FlushFinalBlock(); cacheKey = Base32.GetDigest(hashAlg.Hash); } return(cacheKey); }