@@ -10,31 +10,33 @@ namespace System.Globalization
1010{ 
1111    internal  static   partial  class  Normalization 
1212    { 
13-         private  static   unsafe  bool  IcuIsNormalized ( string   strInput ,  NormalizationForm  normalizationForm ) 
13+         private  static   unsafe  bool  IcuIsNormalized ( ReadOnlySpan < char >   source ,  NormalizationForm  normalizationForm ) 
1414        { 
1515            Debug . Assert ( ! GlobalizationMode . Invariant ) ; 
1616            Debug . Assert ( ! GlobalizationMode . UseNls ) ; 
17+             Debug . Assert ( ! source . IsEmpty ) ; 
18+             Debug . Assert ( normalizationForm  is  NormalizationForm . FormC  or NormalizationForm . FormD  or NormalizationForm . FormKC  or NormalizationForm . FormKD ) ; 
1719
18-             ValidateArguments ( strInput ,  normalizationForm ) ; 
20+             ValidateArguments ( source ,  normalizationForm ,   nameof ( source ) ) ; 
1921
2022            int  ret ; 
21-             fixed ( char *  pInput  =  strInput ) 
23+             fixed ( char *  pInput  =  source ) 
2224            { 
2325#if TARGET_MACCATALYST  ||  TARGET_IOS  ||  TARGET_TVOS 
2426                if  ( GlobalizationMode . Hybrid ) 
2527                { 
26-                     ret  =  Interop . Globalization . IsNormalizedNative ( normalizationForm ,  pInput ,  strInput . Length ) ; 
28+                     ret  =  Interop . Globalization . IsNormalizedNative ( normalizationForm ,  pInput ,  source . Length ) ; 
2729                } 
2830                else 
2931#endif
3032                { 
31-                     ret  =  Interop . Globalization . IsNormalized ( normalizationForm ,  pInput ,  strInput . Length ) ; 
33+                     ret  =  Interop . Globalization . IsNormalized ( normalizationForm ,  pInput ,  source . Length ) ; 
3234                } 
3335            } 
3436
3537            if  ( ret  ==  - 1 ) 
3638            { 
37-                 throw  new  ArgumentException ( SR . Argument_InvalidCharSequenceNoIndex ,  nameof ( strInput ) ) ; 
39+                 throw  new  ArgumentException ( SR . Argument_InvalidCharSequenceNoIndex ,  nameof ( source ) ) ; 
3840            } 
3941
4042            return  ret  ==  1 ; 
@@ -44,6 +46,7 @@ private static unsafe string IcuNormalize(string strInput, NormalizationForm nor
4446        { 
4547            Debug . Assert ( ! GlobalizationMode . Invariant ) ; 
4648            Debug . Assert ( ! GlobalizationMode . UseNls ) ; 
49+             Debug . Assert ( normalizationForm  ==  NormalizationForm . FormC  ||  normalizationForm  ==  NormalizationForm . FormD  ||  normalizationForm  ==  NormalizationForm . FormKC  ||  normalizationForm  ==  NormalizationForm . FormKD ) ; 
4750
4851            ValidateArguments ( strInput ,  normalizationForm ) ; 
4952
@@ -114,25 +117,95 @@ private static unsafe string IcuNormalize(string strInput, NormalizationForm nor
114117            } 
115118        } 
116119
117-         private  static   void   ValidateArguments ( string   strInput ,   NormalizationForm  normalizationForm ) 
120+         private  static   unsafe   bool   IcuTryNormalize ( ReadOnlySpan < char >   source ,   Span < char >   destination ,   out   int   charsWritten ,   NormalizationForm  normalizationForm   =   NormalizationForm . FormC ) 
118121        { 
119-             Debug . Assert ( strInput  !=  null ) ; 
122+             Debug . Assert ( ! GlobalizationMode . Invariant ) ; 
123+             Debug . Assert ( ! GlobalizationMode . UseNls ) ; 
124+             Debug . Assert ( ! source . IsEmpty ) ; 
125+             Debug . Assert ( normalizationForm  ==  NormalizationForm . FormC  ||  normalizationForm  ==  NormalizationForm . FormD  ||  normalizationForm  ==  NormalizationForm . FormKC  ||  normalizationForm  ==  NormalizationForm . FormKD ) ; 
120126
121-             if  ( ( OperatingSystem . IsBrowser ( )   ||   OperatingSystem . IsWasi ( ) ) &&   ( normalizationForm   ==   NormalizationForm . FormKC   ||   normalizationForm   ==   NormalizationForm . FormKD ) ) 
127+             if  ( destination . IsEmpty ) 
122128            { 
123-                 // Browser's ICU doesn't contain data needed for FormKC and FormKD 
124-                 throw  new  PlatformNotSupportedException ( ) ; 
129+                 charsWritten  =  0 ; 
130+                 return  false ; 
131+             } 
132+ 
133+             ValidateArguments ( source ,  normalizationForm ,  nameof ( source ) ) ; 
134+ 
135+             int  realLen ; 
136+             fixed ( char *  pInput  =  source ) 
137+             fixed ( char *  pDest  =  destination ) 
138+             { 
139+ #if TARGET_MACCATALYST  ||  TARGET_IOS  ||  TARGET_TVOS 
140+                 if  ( GlobalizationMode . Hybrid ) 
141+                 { 
142+                     realLen  =  Interop . Globalization . NormalizeStringNative ( normalizationForm ,  pInput ,  source . Length ,  pDest ,  destination . Length ) ; 
143+                 } 
144+                 else 
145+ #endif
146+                 { 
147+                     realLen  =  Interop . Globalization . NormalizeString ( normalizationForm ,  pInput ,  source . Length ,  pDest ,  destination . Length ) ; 
148+                 } 
149+             } 
150+ 
151+             if  ( realLen  <  0 ) 
152+             { 
153+                 throw  new  ArgumentException ( SR . Argument_InvalidCharSequenceNoIndex ,  nameof ( source ) ) ; 
154+             } 
155+ 
156+             if  ( realLen  <=  destination . Length ) 
157+             { 
158+                 charsWritten  =  realLen ; 
159+                 return  true ; 
160+             } 
161+ 
162+             charsWritten  =  0 ; 
163+             return  false ; 
164+         } 
165+ 
166+         private  static   unsafe  int  IcuGetNormalizedLength ( ReadOnlySpan < char >  source ,  NormalizationForm  normalizationForm ) 
167+         { 
168+             Debug . Assert ( ! GlobalizationMode . Invariant ) ; 
169+             Debug . Assert ( ! GlobalizationMode . UseNls ) ; 
170+             Debug . Assert ( ! source . IsEmpty ) ; 
171+             Debug . Assert ( normalizationForm  ==  NormalizationForm . FormC  ||  normalizationForm  ==  NormalizationForm . FormD  ||  normalizationForm  ==  NormalizationForm . FormKC  ||  normalizationForm  ==  NormalizationForm . FormKD ) ; 
172+ 
173+             ValidateArguments ( source ,  normalizationForm ,  nameof ( source ) ) ; 
174+ 
175+             int  realLen ; 
176+             fixed ( char *  pInput  =  source ) 
177+             { 
178+ #if TARGET_MACCATALYST  ||  TARGET_IOS  ||  TARGET_TVOS 
179+                 if  ( GlobalizationMode . Hybrid ) 
180+                 { 
181+                     realLen  =  Interop . Globalization . NormalizeStringNative ( normalizationForm ,  pInput ,  source . Length ,  null ,  0 ) ; 
182+                 } 
183+                 else 
184+ #endif
185+                 { 
186+                     realLen  =  Interop . Globalization . NormalizeString ( normalizationForm ,  pInput ,  source . Length ,  null ,  0 ) ; 
187+                 } 
188+             } 
189+ 
190+             if  ( realLen  <  0 ) 
191+             { 
192+                 throw  new  ArgumentException ( SR . Argument_InvalidCharSequenceNoIndex ,  nameof ( source ) ) ; 
125193            } 
126194
127-             if  ( normalizationForm  !=  NormalizationForm . FormC  &&  normalizationForm  !=  NormalizationForm . FormD  && 
128-                 normalizationForm  !=  NormalizationForm . FormKC  &&  normalizationForm  !=  NormalizationForm . FormKD ) 
195+             return  realLen ; 
196+         } 
197+ 
198+         private  static   void  ValidateArguments ( ReadOnlySpan < char >  strInput ,  NormalizationForm  normalizationForm ,  string  paramName  =  "strInput" ) 
199+         { 
200+             if  ( ( OperatingSystem . IsBrowser ( )  ||  OperatingSystem . IsWasi ( ) )  &&  ( normalizationForm  ==  NormalizationForm . FormKC  ||  normalizationForm  ==  NormalizationForm . FormKD ) ) 
129201            { 
130-                 throw  new  ArgumentException ( SR . Argument_InvalidNormalizationForm ,  nameof ( normalizationForm ) ) ; 
202+                 // Browser's ICU doesn't contain data needed for FormKC and FormKD 
203+                 throw  new  PlatformNotSupportedException ( SR . Argument_UnsupportedNormalizationFormInBrowser ) ; 
131204            } 
132205
133206            if  ( HasInvalidUnicodeSequence ( strInput ) ) 
134207            { 
135-                 throw  new  ArgumentException ( SR . Argument_InvalidCharSequenceNoIndex ,  nameof ( strInput ) ) ; 
208+                 throw  new  ArgumentException ( SR . Argument_InvalidCharSequenceNoIndex ,  paramName ) ; 
136209            } 
137210        } 
138211
@@ -143,7 +216,7 @@ private static void ValidateArguments(string strInput, NormalizationForm normali
143216        /// We walk the string ourselves looking for these bad sequences so we can continue to throw 
144217        /// ArgumentException in these cases. 
145218        /// </summary> 
146-         private  static   bool  HasInvalidUnicodeSequence ( string  s ) 
219+         private  static   bool  HasInvalidUnicodeSequence ( ReadOnlySpan < char >  s ) 
147220        { 
148221            for  ( int  i  =  0 ;  i  <  s . Length ;  i ++ ) 
149222            { 
0 commit comments