@@ -127,6 +127,84 @@ public static partial class UnicodeUtil
127127 private const int SURROGATE_OFFSET = Character . MinSupplementaryCodePoint -
128128 ( UNI_SUR_HIGH_START << ( int ) HALF_SHIFT ) - UNI_SUR_LOW_START ;
129129
130+ /// <summary>
131+ /// Encodes into a span of bytes a set of characters from the specified read-only span if the
132+ /// <paramref name="destination"/> is large enough.
133+ /// </summary>
134+ /// <param name="source">The span containing the set of characters to encode.</param>
135+ /// <param name="destination">The byte span to hold the encoded bytes.</param>
136+ /// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes
137+ /// encoded into <paramref name="destination"/>.</param>
138+ /// <returns><c>true</c> if all of the characters were encoded into the destination;
139+ /// <c>false</c> if the destination was too small to contain all the encoded bytes.</returns>
140+ /// <remarks>To estimate the number of bytes to allocate, use <see cref="GetMaxByteCount(int)"/>.
141+ /// <para/>
142+ /// This is similar to <c>Encoding.UTF8.TryGetBytes()</c>.</remarks>
143+ // LUCENENET specific overload
144+ public static bool TryUTF16toUTF8 ( ReadOnlySpan < char > source , Span < byte > destination , out int bytesWritten )
145+ {
146+ bytesWritten = 0 ;
147+ int offset = 0 ;
148+ int length = source . Length ;
149+ int end = offset + length ;
150+
151+ int upto = 0 ;
152+ for ( int i = offset ; i < end ; i ++ )
153+ {
154+ var code = ( int ) source [ i ] ;
155+
156+ if ( code < 0x80 )
157+ {
158+ if ( upto + 1 >= destination . Length ) return false ;
159+ destination [ upto ++ ] = ( byte ) code ;
160+ }
161+ else if ( code < 0x800 )
162+ {
163+ if ( upto + 2 >= destination . Length ) return false ;
164+ destination [ upto ++ ] = ( byte ) ( 0xC0 | ( code >> 6 ) ) ;
165+ destination [ upto ++ ] = ( byte ) ( 0x80 | ( code & 0x3F ) ) ;
166+ }
167+ else if ( code < 0xD800 || code > 0xDFFF )
168+ {
169+ if ( upto + 3 >= destination . Length ) return false ;
170+ destination [ upto ++ ] = ( byte ) ( 0xE0 | ( code >> 12 ) ) ;
171+ destination [ upto ++ ] = ( byte ) ( 0x80 | ( ( code >> 6 ) & 0x3F ) ) ;
172+ destination [ upto ++ ] = ( byte ) ( 0x80 | ( code & 0x3F ) ) ;
173+ }
174+ else
175+ {
176+ // surrogate pair
177+ // confirm valid high surrogate
178+ if ( code < 0xDC00 && ( i < end - 1 ) )
179+ {
180+ var utf32 = ( int ) source [ i + 1 ] ;
181+ // confirm valid low surrogate and write pair
182+ if ( utf32 >= 0xDC00 && utf32 <= 0xDFFF )
183+ {
184+ utf32 = ( code << 10 ) + utf32 + SURROGATE_OFFSET ;
185+ i ++ ;
186+ if ( upto + 4 >= destination . Length ) return false ;
187+ destination [ upto ++ ] = ( byte ) ( 0xF0 | ( utf32 >> 18 ) ) ;
188+ destination [ upto ++ ] = ( byte ) ( 0x80 | ( ( utf32 >> 12 ) & 0x3F ) ) ;
189+ destination [ upto ++ ] = ( byte ) ( 0x80 | ( ( utf32 >> 6 ) & 0x3F ) ) ;
190+ destination [ upto ++ ] = ( byte ) ( 0x80 | ( utf32 & 0x3F ) ) ;
191+ continue ;
192+ }
193+ }
194+
195+ // replace unpaired surrogate or out-of-order low surrogate
196+ // with substitution character
197+ if ( upto + 3 >= destination . Length ) return false ;
198+ destination [ upto ++ ] = 0xEF ;
199+ destination [ upto ++ ] = 0xBF ;
200+ destination [ upto ++ ] = 0xBD ;
201+ }
202+ }
203+
204+ bytesWritten = upto ;
205+ return true ;
206+ }
207+
130208 /// <summary>
131209 /// Encode characters from a <see cref="ReadOnlySpan{T}"/> (with generic type argument <see cref="char"/>) <paramref name="source"/>, starting at
132210 /// and ending at <paramref name="result"/>. After encoding, <c>result.Offset</c> will always be 0.
@@ -620,6 +698,17 @@ public static bool ValidUTF16String(ReadOnlySpan<char> s, int size)
620698 return true ;
621699 }
622700
701+ /// <summary>
702+ /// Calculates the maximum number of bytes produced by UTF8 encoding the
703+ /// specified number of characters.
704+ /// </summary>
705+ /// <param name="charCount">The number of characters to encode.</param>
706+ /// <returns>The maximum number of bytes produced by encoding the specified
707+ /// number of characters to UTF8.</returns>
708+ /// <remarks>The return value will always be a power of 2.</remarks>
709+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
710+ public static int GetMaxByteCount ( int charCount ) => charCount * 4 ;
711+
623712 // Borrowed from Python's 3.1.2 sources,
624713 // Objects/unicodeobject.c, and modified (see commented
625714 // out section, and the -1s) to disallow the reserved for
0 commit comments