@@ -163,26 +163,52 @@ function addMap(depMap, id, prop, dependency) {
163163 callbacks . push ( dependency ) ;
164164}
165165
166- function addPattern ( depMap , idSpec , prop , dependency ) {
166+ // Patterns are stored in a nested Map structure to avoid the overhead of
167+ // stringifying ids for every callback.
168+ function addPattern ( patterns , idSpec , prop , dependency ) {
167169 const keys = Object . keys ( idSpec ) . sort ( ) ;
168170 const keyStr = keys . join ( ',' ) ;
169171 const values = props ( keys , idSpec ) ;
170- const keyCallbacks = ( depMap [ keyStr ] = depMap [ keyStr ] || { } ) ;
171- const propCallbacks = ( keyCallbacks [ prop ] = keyCallbacks [ prop ] || [ ] ) ;
172- let valMatch = false ;
173- for ( let i = 0 ; i < propCallbacks . length ; i ++ ) {
174- if ( equals ( values , propCallbacks [ i ] . values ) ) {
175- valMatch = propCallbacks [ i ] ;
176- break ;
177- }
172+ const valuesKey = values
173+ . map ( v =>
174+ typeof v === 'object' && v !== null
175+ ? v . wild
176+ ? v . wild
177+ : JSON . stringify ( v )
178+ : String ( v )
179+ )
180+ . join ( '|' ) ;
181+
182+ if ( ! patterns . has ( keyStr ) ) {
183+ patterns . set ( keyStr , new Map ( ) ) ;
184+ }
185+ const propMap = patterns . get ( keyStr ) ;
186+ if ( ! propMap . has ( prop ) ) {
187+ propMap . set ( prop , new Map ( ) ) ;
178188 }
189+ const valueMap = propMap . get ( prop ) ;
190+
191+ let valMatch = valueMap . get ( valuesKey ) ;
179192 if ( ! valMatch ) {
180193 valMatch = { keys, values, callbacks : [ ] } ;
181- propCallbacks . push ( valMatch ) ;
194+ valueMap . set ( valuesKey , valMatch ) ;
182195 }
183196 valMatch . callbacks . push ( dependency ) ;
184197}
185198
199+ // Convert the nested Map structure of patterns into the plain nested object structure
200+ // expected by the rest of the code, with stringified id keys.
201+ // This is only done once per pattern, at the end of graph construction,
202+ // to minimize the overhead of stringifying ids.
203+ function offloadPatterns ( patternsMap , targetMap ) {
204+ for ( const [ keyStr , propMap ] of patternsMap . entries ( ) ) {
205+ targetMap [ keyStr ] = { } ;
206+ for ( const [ prop , valueMap ] of propMap . entries ( ) ) {
207+ targetMap [ keyStr ] [ prop ] = Array . from ( valueMap . values ( ) ) ;
208+ }
209+ }
210+ }
211+
186212function validateDependencies ( parsedDependencies , dispatchError ) {
187213 const outStrs = { } ;
188214 const outObjs = [ ] ;
@@ -686,8 +712,10 @@ export function computeGraphs(dependencies, dispatchError, config) {
686712 */
687713 const outputMap = { } ;
688714 const inputMap = { } ;
689- const outputPatterns = { } ;
690- const inputPatterns = { } ;
715+ const outputPatternMap = new Map ( ) ;
716+ const inputPatternMap = new Map ( ) ;
717+ let outputPatterns = { } ;
718+ let inputPatterns = { } ;
691719
692720 const finalGraphs = {
693721 MultiGraph : multiGraph ,
@@ -704,12 +732,14 @@ export function computeGraphs(dependencies, dispatchError, config) {
704732 return finalGraphs ;
705733 }
706734
735+ // builds up wildcardPlaceholders with all the wildcard keys and values used in the callbacks, so we can generate the full list of ids that each callback depends on.
707736 parsedDependencies . forEach ( dependency => {
708737 const { outputs, inputs} = dependency ;
709738
710- outputs . concat ( inputs ) . forEach ( item => {
711- const { id} = item ;
712- if ( typeof id === 'object' ) {
739+ outputs
740+ . concat ( inputs )
741+ . filter ( item => typeof item . id === 'object' )
742+ . forEach ( item => {
713743 forEachObjIndexed ( ( val , key ) => {
714744 if ( ! wildcardPlaceholders [ key ] ) {
715745 wildcardPlaceholders [ key ] = {
@@ -725,11 +755,11 @@ export function computeGraphs(dependencies, dispatchError, config) {
725755 } else if ( keyPlaceholders . exact . indexOf ( val ) === - 1 ) {
726756 keyPlaceholders . exact . push ( val ) ;
727757 }
728- } , id ) ;
729- }
730- } ) ;
758+ } , item . id ) ;
759+ } ) ;
731760 } ) ;
732761
762+ // Efficiently build wildcardPlaceholders.vals arrays
733763 forEachObjIndexed ( keyPlaceholders => {
734764 const { exact, expand} = keyPlaceholders ;
735765 const vals = exact . slice ( ) . sort ( idValSort ) ;
@@ -811,7 +841,7 @@ export function computeGraphs(dependencies, dispatchError, config) {
811841 const cbOut = [ ] ;
812842
813843 function addInputToMulti ( inIdProp , outIdProp , firstPass = true ) {
814- if ( ! config . validate_callbacks ) return
844+ if ( ! config . validate_callbacks ) return ;
815845 multiGraph . addNode ( inIdProp ) ;
816846 multiGraph . addDependency ( inIdProp , outIdProp ) ;
817847 // only store callback inputs and outputs during the first pass
@@ -829,7 +859,7 @@ export function computeGraphs(dependencies, dispatchError, config) {
829859 cbOut . push ( [ ] ) ;
830860
831861 function addOutputToMulti ( outIdFinal , outIdProp ) {
832- if ( ! config . validate_callbacks ) return
862+ if ( ! config . validate_callbacks ) return ;
833863 multiGraph . addNode ( outIdProp ) ;
834864 inputs . forEach ( inObj => {
835865 const { id : inId , property} = inObj ;
@@ -866,7 +896,7 @@ export function computeGraphs(dependencies, dispatchError, config) {
866896 // check if this output is also an input to the same callback
867897 let alsoInput ;
868898 if ( config . validate_callbacks ) {
869- alsoInput = checkInOutOverlap ( outIdProp , inputs ) ;
899+ alsoInput = checkInOutOverlap ( outIdProp , inputs ) ;
870900 }
871901 if ( typeof outId === 'object' ) {
872902 if ( config . validate_callbacks ) {
@@ -882,7 +912,7 @@ export function computeGraphs(dependencies, dispatchError, config) {
882912 addOutputToMulti ( id , outIdName ) ;
883913 } ) ;
884914 }
885- addPattern ( outputPatterns , outId , property , finalDependency ) ;
915+ addPattern ( outputPatternMap , outId , property , finalDependency ) ;
886916 } else {
887917 if ( config . validate_callbacks ) {
888918 let outIdName = combineIdAndProp ( outIdProp ) ;
@@ -900,12 +930,14 @@ export function computeGraphs(dependencies, dispatchError, config) {
900930 inputs . forEach ( inputObject => {
901931 const { id : inId , property : inProp } = inputObject ;
902932 if ( typeof inId === 'object' ) {
903- addPattern ( inputPatterns , inId , inProp , finalDependency ) ;
933+ addPattern ( inputPatternMap , inId , inProp , finalDependency ) ;
904934 } else {
905935 addMap ( inputMap , inId , inProp , finalDependency ) ;
906936 }
907937 } ) ;
908938 } ) ;
939+ outputPatterns = offloadPatterns ( outputPatternMap , outputPatterns ) ;
940+ inputPatterns = offloadPatterns ( inputPatternMap , inputPatterns ) ;
909941
910942 // second pass for adding new output nodes as dependencies where needed
911943 duplicateOutputs . forEach ( dupeOutIdProp => {
0 commit comments