@@ -17,6 +17,7 @@ const {
1717 PromisePrototypeThen,
1818 PromiseReject,
1919 PromiseWithResolvers,
20+ SafeMap,
2021 SafeSet,
2122 StringPrototypeToUpperCase,
2223 Symbol,
@@ -455,8 +456,11 @@ const experimentalAlgorithms = [
455456] ;
456457
457458// Transform the algorithm definitions into the operation-keyed structure
459+ // Also builds a parallel Map<UPPERCASED_NAME, canonicalName> per operation
460+ // for O(1) case-insensitive algorithm name lookup in normalizeAlgorithm.
458461function createSupportedAlgorithms ( algorithmDefs ) {
459462 const result = { } ;
463+ const nameMap = { } ;
460464
461465 for ( const { 0 : algorithmName , 1 : operations } of ObjectEntries ( algorithmDefs ) ) {
462466 // Skip algorithms that are conditionally not supported
@@ -467,6 +471,8 @@ function createSupportedAlgorithms(algorithmDefs) {
467471
468472 for ( const { 0 : operation , 1 : dict } of ObjectEntries ( operations ) ) {
469473 result [ operation ] ||= { } ;
474+ nameMap [ operation ] ||= new SafeMap ( ) ;
475+ nameMap [ operation ] . set ( StringPrototypeToUpperCase ( algorithmName ) , algorithmName ) ;
470476
471477 // Add experimental warnings for experimental algorithms
472478 if ( ArrayPrototypeIncludes ( experimentalAlgorithms , algorithmName ) ) {
@@ -484,10 +490,11 @@ function createSupportedAlgorithms(algorithmDefs) {
484490 }
485491 }
486492
487- return result ;
493+ return { algorithms : result , nameMap } ;
488494}
489495
490- const kSupportedAlgorithms = createSupportedAlgorithms ( kAlgorithmDefinitions ) ;
496+ const { algorithms : kSupportedAlgorithms , nameMap : kAlgorithmNameMap } =
497+ createSupportedAlgorithms ( kAlgorithmDefinitions ) ;
491498
492499const simpleAlgorithmDictionaries = {
493500 AesCbcParams : { iv : 'BufferSource' } ,
@@ -529,6 +536,12 @@ const simpleAlgorithmDictionaries = {
529536 TurboShakeParams : { } ,
530537} ;
531538
539+ // Pre-compute ObjectKeys() for each dictionary entry at module init
540+ // to avoid allocating a new keys array on every normalizeAlgorithm call.
541+ for ( const { 0 : name , 1 : types } of ObjectEntries ( simpleAlgorithmDictionaries ) ) {
542+ simpleAlgorithmDictionaries [ name ] = { keys : ObjectKeys ( types ) , types } ;
543+ }
544+
532545function validateMaxBufferLength ( data , name , max = kMaxBufferLength ) {
533546 if ( data . byteLength > max ) {
534547 throw lazyDOMException (
@@ -539,6 +552,14 @@ function validateMaxBufferLength(data, name, max = kMaxBufferLength) {
539552
540553let webidl ;
541554
555+ // Keep this as a regular object. The WebIDL converters read and spread these
556+ // options on the normalizeAlgorithm hot path, and a null-prototype object
557+ // measurably regresses benchmark/misc/webcrypto-webidl normalizeAlgorithm-*.
558+ const kNormalizeAlgorithmOpts = {
559+ prefix : 'Failed to normalize algorithm' ,
560+ context : 'passed algorithm' ,
561+ } ;
562+
542563// https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm
543564// adapted for Node.js from Deno's implementation
544565// https://github.com/denoland/deno/blob/v1.29.1/ext/crypto/00_crypto.js#L195
@@ -551,69 +572,56 @@ function normalizeAlgorithm(algorithm, op) {
551572 // 1.
552573 const registeredAlgorithms = kSupportedAlgorithms [ op ] ;
553574 // 2. 3.
554- const initialAlg = webidl . converters . Algorithm ( algorithm , {
555- prefix : 'Failed to normalize algorithm' ,
556- context : 'passed algorithm' ,
557- } ) ;
575+ const initialAlg = webidl . converters . Algorithm ( algorithm ,
576+ kNormalizeAlgorithmOpts ) ;
558577 // 4.
559578 let algName = initialAlg . name ;
560579
561- // 5.
562- let desiredType ;
563- for ( const key in registeredAlgorithms ) {
564- if ( ! ObjectPrototypeHasOwnProperty ( registeredAlgorithms , key ) ) {
565- continue ;
566- }
567- if (
568- StringPrototypeToUpperCase ( key ) === StringPrototypeToUpperCase ( algName )
569- ) {
570- algName = key ;
571- desiredType = registeredAlgorithms [ key ] ;
572- }
573- }
574- if ( desiredType === undefined )
580+ // 5. Case-insensitive lookup via pre-built Map (O(1) instead of O(n)).
581+ const canonicalName = kAlgorithmNameMap [ op ] ?. get (
582+ StringPrototypeToUpperCase ( algName ) ) ;
583+ if ( canonicalName === undefined )
575584 throw lazyDOMException ( 'Unrecognized algorithm name' , 'NotSupportedError' ) ;
576585
586+ algName = canonicalName ;
587+ const desiredType = registeredAlgorithms [ algName ] ;
588+
577589 // Fast path everything below if the registered dictionary is null
578590 if ( desiredType === null )
579591 return { name : algName } ;
580592
581593 // 6.
582594 const normalizedAlgorithm = webidl . converters [ desiredType ] (
583595 { __proto__ : algorithm , name : algName } ,
584- {
585- prefix : 'Failed to normalize algorithm' ,
586- context : 'passed algorithm' ,
587- } ,
596+ kNormalizeAlgorithmOpts ,
588597 ) ;
589598 // 7.
590599 normalizedAlgorithm . name = algName ;
591600
592- // 9.
593- const dict = simpleAlgorithmDictionaries [ desiredType ] ;
594- // 10.
595- const dictKeys = dict ? ObjectKeys ( dict ) : [ ] ;
596- for ( let i = 0 ; i < dictKeys . length ; i ++ ) {
597- const member = dictKeys [ i ] ;
598- if ( ! ObjectPrototypeHasOwnProperty ( dict , member ) )
599- continue ;
600- const idlType = dict [ member ] ;
601- const idlValue = normalizedAlgorithm [ member ] ;
602- // 3.
603- if ( idlType === 'BufferSource' && idlValue ) {
604- const isView = ArrayBufferIsView ( idlValue ) ;
605- normalizedAlgorithm [ member ] = TypedArrayPrototypeSlice (
606- new Uint8Array (
607- isView ? getDataViewOrTypedArrayBuffer ( idlValue ) : idlValue ,
608- isView ? getDataViewOrTypedArrayByteOffset ( idlValue ) : 0 ,
609- isView ? getDataViewOrTypedArrayByteLength ( idlValue ) : ArrayBufferPrototypeGetByteLength ( idlValue ) ,
610- ) ,
611- ) ;
612- } else if ( idlType === 'HashAlgorithmIdentifier' ) {
613- normalizedAlgorithm [ member ] = normalizeAlgorithm ( idlValue , 'digest' ) ;
614- } else if ( idlType === 'AlgorithmIdentifier' ) {
615- // This extension point is not used by any supported algorithm (yet?)
616- throw lazyDOMException ( 'Not implemented.' , 'NotSupportedError' ) ;
601+ // 9. 10. Pre-computed keys and types from simpleAlgorithmDictionaries.
602+ const dictMeta = simpleAlgorithmDictionaries [ desiredType ] ;
603+ if ( dictMeta ) {
604+ const { keys : dictKeys , types : dictTypes } = dictMeta ;
605+ for ( let i = 0 ; i < dictKeys . length ; i ++ ) {
606+ const member = dictKeys [ i ] ;
607+ const idlType = dictTypes [ member ] ;
608+ const idlValue = normalizedAlgorithm [ member ] ;
609+ // 3.
610+ if ( idlType === 'BufferSource' && idlValue ) {
611+ const isView = ArrayBufferIsView ( idlValue ) ;
612+ normalizedAlgorithm [ member ] = TypedArrayPrototypeSlice (
613+ new Uint8Array (
614+ isView ? getDataViewOrTypedArrayBuffer ( idlValue ) : idlValue ,
615+ isView ? getDataViewOrTypedArrayByteOffset ( idlValue ) : 0 ,
616+ isView ? getDataViewOrTypedArrayByteLength ( idlValue ) : ArrayBufferPrototypeGetByteLength ( idlValue ) ,
617+ ) ,
618+ ) ;
619+ } else if ( idlType === 'HashAlgorithmIdentifier' ) {
620+ normalizedAlgorithm [ member ] = normalizeAlgorithm ( idlValue , 'digest' ) ;
621+ } else if ( idlType === 'AlgorithmIdentifier' ) {
622+ // This extension point is not used by any supported algorithm (yet?)
623+ throw lazyDOMException ( 'Not implemented.' , 'NotSupportedError' ) ;
624+ }
617625 }
618626 }
619627
0 commit comments