@@ -16,6 +16,7 @@ const {
1616 ObjectKeys,
1717 ObjectPrototypeHasOwnProperty,
1818 PromiseWithResolvers,
19+ SafeMap,
1920 SafeSet,
2021 StringPrototypeToUpperCase,
2122 Symbol,
@@ -453,8 +454,11 @@ const experimentalAlgorithms = [
453454] ;
454455
455456// Transform the algorithm definitions into the operation-keyed structure
457+ // Also builds a parallel Map<UPPERCASED_NAME, canonicalName> per operation
458+ // for O(1) case-insensitive algorithm name lookup in normalizeAlgorithm.
456459function createSupportedAlgorithms ( algorithmDefs ) {
457460 const result = { } ;
461+ const nameMap = { } ;
458462
459463 for ( const { 0 : algorithmName , 1 : operations } of ObjectEntries ( algorithmDefs ) ) {
460464 // Skip algorithms that are conditionally not supported
@@ -465,6 +469,8 @@ function createSupportedAlgorithms(algorithmDefs) {
465469
466470 for ( const { 0 : operation , 1 : dict } of ObjectEntries ( operations ) ) {
467471 result [ operation ] ||= { } ;
472+ nameMap [ operation ] ||= new SafeMap ( ) ;
473+ nameMap [ operation ] . set ( StringPrototypeToUpperCase ( algorithmName ) , algorithmName ) ;
468474
469475 // Add experimental warnings for experimental algorithms
470476 if ( ArrayPrototypeIncludes ( experimentalAlgorithms , algorithmName ) ) {
@@ -482,10 +488,11 @@ function createSupportedAlgorithms(algorithmDefs) {
482488 }
483489 }
484490
485- return result ;
491+ return { algorithms : result , nameMap } ;
486492}
487493
488- const kSupportedAlgorithms = createSupportedAlgorithms ( kAlgorithmDefinitions ) ;
494+ const { algorithms : kSupportedAlgorithms , nameMap : kAlgorithmNameMap } =
495+ createSupportedAlgorithms ( kAlgorithmDefinitions ) ;
489496
490497const simpleAlgorithmDictionaries = {
491498 AesCbcParams : { iv : 'BufferSource' } ,
@@ -527,6 +534,12 @@ const simpleAlgorithmDictionaries = {
527534 TurboShakeParams : { } ,
528535} ;
529536
537+ // Pre-compute ObjectKeys() for each dictionary entry at module init
538+ // to avoid allocating a new keys array on every normalizeAlgorithm call.
539+ for ( const { 0 : name , 1 : types } of ObjectEntries ( simpleAlgorithmDictionaries ) ) {
540+ simpleAlgorithmDictionaries [ name ] = { keys : ObjectKeys ( types ) , types } ;
541+ }
542+
530543function validateMaxBufferLength ( data , name , max = kMaxBufferLength ) {
531544 if ( data . byteLength > max ) {
532545 throw lazyDOMException (
@@ -537,6 +550,14 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
537550
538551let webidl ;
539552
553+ // Keep this as a regular object. The WebIDL converters read and spread these
554+ // options on the normalizeAlgorithm hot path, and a null-prototype object
555+ // measurably regresses benchmark/misc/webcrypto-webidl normalizeAlgorithm-*.
556+ const kNormalizeAlgorithmOpts = {
557+ prefix : 'Failed to normalize algorithm' ,
558+ context : 'passed algorithm' ,
559+ } ;
560+
540561// https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
541562// adapted for Node.js from Deno's implementation
542563// https://github.com/denoland/deno/blob/v1.29.1/ext/crypto/00_crypto.js#L195
@@ -549,69 +570,56 @@ function normalizeAlgorithm(algorithm, op) {
549570 // 1.
550571 const registeredAlgorithms = kSupportedAlgorithms [ op ] ;
551572 // 2. 3.
552- const initialAlg = webidl . converters . Algorithm ( algorithm , {
553- prefix : 'Failed to normalize algorithm' ,
554- context : 'passed algorithm' ,
555- } ) ;
573+ const initialAlg = webidl . converters . Algorithm ( algorithm ,
574+ kNormalizeAlgorithmOpts ) ;
556575 // 4.
557576 let algName = initialAlg . name ;
558577
559- // 5.
560- let desiredType ;
561- for ( const key in registeredAlgorithms ) {
562- if ( ! ObjectPrototypeHasOwnProperty ( registeredAlgorithms , key ) ) {
563- continue ;
564- }
565- if (
566- StringPrototypeToUpperCase ( key ) === StringPrototypeToUpperCase ( algName )
567- ) {
568- algName = key ;
569- desiredType = registeredAlgorithms [ key ] ;
570- }
571- }
572- if ( desiredType === undefined )
578+ // 5. Case-insensitive lookup via pre-built Map (O(1) instead of O(n)).
579+ const canonicalName = kAlgorithmNameMap [ op ] ?. get (
580+ StringPrototypeToUpperCase ( algName ) ) ;
581+ if ( canonicalName === undefined )
573582 throw lazyDOMException ( 'Unrecognized algorithm name' , 'NotSupportedError' ) ;
574583
584+ algName = canonicalName ;
585+ const desiredType = registeredAlgorithms [ algName ] ;
586+
575587 // Fast path everything below if the registered dictionary is null
576588 if ( desiredType === null )
577589 return { name : algName } ;
578590
579591 // 6.
580592 const normalizedAlgorithm = webidl . converters [ desiredType ] (
581593 { __proto__ : algorithm , name : algName } ,
582- {
583- prefix : 'Failed to normalize algorithm' ,
584- context : 'passed algorithm' ,
585- } ,
594+ kNormalizeAlgorithmOpts ,
586595 ) ;
587596 // 7.
588597 normalizedAlgorithm . name = algName ;
589598
590- // 9.
591- const dict = simpleAlgorithmDictionaries [ desiredType ] ;
592- // 10.
593- const dictKeys = dict ? ObjectKeys ( dict ) : [ ] ;
594- for ( let i = 0 ; i < dictKeys . length ; i ++ ) {
595- const member = dictKeys [ i ] ;
596- if ( ! ObjectPrototypeHasOwnProperty ( dict , member ) )
597- continue ;
598- const idlType = dict [ member ] ;
599- const idlValue = normalizedAlgorithm [ member ] ;
600- // 3.
601- if ( idlType === 'BufferSource' && idlValue ) {
602- const isView = ArrayBufferIsView ( idlValue ) ;
603- normalizedAlgorithm [ member ] = TypedArrayPrototypeSlice (
604- new Uint8Array (
605- isView ? getDataViewOrTypedArrayBuffer ( idlValue ) : idlValue ,
606- isView ? getDataViewOrTypedArrayByteOffset ( idlValue ) : 0 ,
607- isView ? getDataViewOrTypedArrayByteLength ( idlValue ) : ArrayBufferPrototypeGetByteLength ( idlValue ) ,
608- ) ,
609- ) ;
610- } else if ( idlType === 'HashAlgorithmIdentifier' ) {
611- normalizedAlgorithm [ member ] = normalizeAlgorithm ( idlValue , 'digest' ) ;
612- } else if ( idlType === 'AlgorithmIdentifier' ) {
613- // This extension point is not used by any supported algorithm (yet?)
614- throw lazyDOMException ( 'Not implemented.' , 'NotSupportedError' ) ;
599+ // 9. 10. Pre-computed keys and types from simpleAlgorithmDictionaries.
600+ const dictMeta = simpleAlgorithmDictionaries [ desiredType ] ;
601+ if ( dictMeta ) {
602+ const { keys : dictKeys , types : dictTypes } = dictMeta ;
603+ for ( let i = 0 ; i < dictKeys . length ; i ++ ) {
604+ const member = dictKeys [ i ] ;
605+ const idlType = dictTypes [ member ] ;
606+ const idlValue = normalizedAlgorithm [ member ] ;
607+ // 3.
608+ if ( idlType === 'BufferSource' && idlValue ) {
609+ const isView = ArrayBufferIsView ( idlValue ) ;
610+ normalizedAlgorithm [ member ] = TypedArrayPrototypeSlice (
611+ new Uint8Array (
612+ isView ? getDataViewOrTypedArrayBuffer ( idlValue ) : idlValue ,
613+ isView ? getDataViewOrTypedArrayByteOffset ( idlValue ) : 0 ,
614+ isView ? getDataViewOrTypedArrayByteLength ( idlValue ) : ArrayBufferPrototypeGetByteLength ( idlValue ) ,
615+ ) ,
616+ ) ;
617+ } else if ( idlType === 'HashAlgorithmIdentifier' ) {
618+ normalizedAlgorithm [ member ] = normalizeAlgorithm ( idlValue , 'digest' ) ;
619+ } else if ( idlType === 'AlgorithmIdentifier' ) {
620+ // This extension point is not used by any supported algorithm (yet?)
621+ throw lazyDOMException ( 'Not implemented.' , 'NotSupportedError' ) ;
622+ }
615623 }
616624 }
617625
0 commit comments