@@ -53,227 +53,3 @@ describe('FieldMask', () => {
5353 ) ;
5454 } ) ;
5555} ) ;
56-
57- describe ( 'CompareUtf8Strings' , ( ) => {
58- it ( 'compareUtf8Strings should return correct results' , ( ) => {
59- const errors = [ ] ;
60- const seed = Math . floor ( Math . random ( ) * Number . MAX_SAFE_INTEGER ) ;
61- let passCount = 0 ;
62- const stringGenerator = new StringGenerator ( new Random ( seed ) , 0.33 , 20 ) ;
63- const stringPairGenerator = new StringPairGenerator ( stringGenerator ) ;
64-
65- for ( let i = 0 ; i < 1000000 && errors . length < 10 ; i ++ ) {
66- const { s1, s2 } = stringPairGenerator . next ( ) ;
67-
68- const actual = compareUtf8Strings ( s1 , s2 ) ;
69- const expected = Buffer . from ( s1 , 'utf8' ) . compare ( Buffer . from ( s2 , 'utf8' ) ) ;
70-
71- if ( actual === expected ) {
72- passCount ++ ;
73- } else {
74- errors . push (
75- `compareUtf8Strings(s1="${ s1 } ", s2="${ s2 } ") returned ${ actual } , ` +
76- `but expected ${ expected } (i=${ i } , s1.length=${ s1 . length } , s2.length=${ s2 . length } )`
77- ) ;
78- }
79- }
80-
81- if ( errors . length > 0 ) {
82- console . error (
83- `${ errors . length } test cases failed, ${ passCount } test cases passed, seed=${ seed } ;`
84- ) ;
85- errors . forEach ( ( error , index ) =>
86- console . error ( `errors[${ index } ]: ${ error } ` )
87- ) ;
88- throw new Error ( 'Test failed' ) ;
89- }
90- } ) . timeout ( 20000 ) ;
91-
92- class StringPair {
93- constructor ( readonly s1 : string , readonly s2 : string ) { }
94- }
95-
96- class StringPairGenerator {
97- constructor ( private stringGenerator : StringGenerator ) { }
98-
99- next ( ) : StringPair {
100- const prefix = this . stringGenerator . next ( ) ;
101- const s1 = prefix + this . stringGenerator . next ( ) ;
102- const s2 = prefix + this . stringGenerator . next ( ) ;
103- return new StringPair ( s1 , s2 ) ;
104- }
105- }
106-
107- class StringGenerator {
108- private static readonly DEFAULT_SURROGATE_PAIR_PROBABILITY = 0.33 ;
109- private static readonly DEFAULT_MAX_LENGTH = 20 ;
110-
111- // Pseudo-random number generator. Seed can be set for repeatable tests.
112- private readonly rnd : Random ;
113- private readonly surrogatePairProbability : number ;
114- private readonly maxLength : number ;
115-
116- constructor ( seed : number ) ;
117- constructor (
118- rnd : Random ,
119- surrogatePairProbability : number ,
120- maxLength : number
121- ) ;
122- constructor (
123- seedOrRnd : number | Random ,
124- surrogatePairProbability ?: number ,
125- maxLength ?: number
126- ) {
127- if ( typeof seedOrRnd === 'number' ) {
128- this . rnd = new Random ( seedOrRnd ) ;
129- this . surrogatePairProbability =
130- StringGenerator . DEFAULT_SURROGATE_PAIR_PROBABILITY ;
131- this . maxLength = StringGenerator . DEFAULT_MAX_LENGTH ;
132- } else {
133- this . rnd = seedOrRnd ;
134- this . surrogatePairProbability = StringGenerator . validateProbability (
135- surrogatePairProbability !
136- ) ;
137- this . maxLength = StringGenerator . validateLength ( maxLength ! ) ;
138- }
139- }
140-
141- private static validateProbability ( probability : number ) : number {
142- if ( ! Number . isFinite ( probability ) ) {
143- throw new Error (
144- `invalid surrogate pair probability: ${ probability } (must be between 0.0 and 1.0, inclusive)`
145- ) ;
146- } else if ( probability < 0.0 ) {
147- throw new Error (
148- `invalid surrogate pair probability: ${ probability } (must be greater than or equal to zero)`
149- ) ;
150- } else if ( probability > 1.0 ) {
151- throw new Error (
152- `invalid surrogate pair probability: ${ probability } (must be less than or equal to 1)`
153- ) ;
154- }
155- return probability ;
156- }
157-
158- private static validateLength ( length : number ) : number {
159- if ( length < 0 ) {
160- throw new Error (
161- `invalid maximum string length: ${ length } (must be greater than or equal to zero)`
162- ) ;
163- }
164- return length ;
165- }
166-
167- next ( ) : string {
168- const length = this . rnd . nextInt ( this . maxLength + 1 ) ;
169- const sb = new StringBuilder ( ) ;
170- while ( sb . length ( ) < length ) {
171- const codePoint = this . nextCodePoint ( ) ;
172- sb . appendCodePoint ( codePoint ) ;
173- }
174- return sb . toString ( ) ;
175- }
176-
177- private isNextSurrogatePair ( ) : boolean {
178- return StringGenerator . nextBoolean (
179- this . rnd ,
180- this . surrogatePairProbability
181- ) ;
182- }
183-
184- private static nextBoolean ( rnd : Random , probability : number ) : boolean {
185- if ( probability === 0.0 ) {
186- return false ;
187- } else if ( probability === 1.0 ) {
188- return true ;
189- } else {
190- return rnd . nextFloat ( ) < probability ;
191- }
192- }
193-
194- private nextCodePoint ( ) : number {
195- if ( this . isNextSurrogatePair ( ) ) {
196- return this . nextSurrogateCodePoint ( ) ;
197- } else {
198- return this . nextNonSurrogateCodePoint ( ) ;
199- }
200- }
201-
202- private nextSurrogateCodePoint ( ) : number {
203- const highSurrogateMin = 0xd800 ;
204- const highSurrogateMax = 0xdbff ;
205- const lowSurrogateMin = 0xdc00 ;
206- const lowSurrogateMax = 0xdfff ;
207-
208- const highSurrogate = this . nextCodePointRange (
209- highSurrogateMin ,
210- highSurrogateMax
211- ) ;
212- const lowSurrogate = this . nextCodePointRange (
213- lowSurrogateMin ,
214- lowSurrogateMax
215- ) ;
216-
217- return (
218- ( highSurrogate - 0xd800 ) * 0x400 + ( lowSurrogate - 0xdc00 ) + 0x10000
219- ) ;
220- }
221-
222- private nextNonSurrogateCodePoint ( ) : number {
223- let codePoint ;
224- do {
225- codePoint = this . nextCodePointRange ( 0 , 0xffff ) ; // BMP range
226- } while ( codePoint >= 0xd800 && codePoint <= 0xdfff ) ; // Exclude surrogate range
227-
228- return codePoint ;
229- }
230-
231- private nextCodePointRange ( min : number , max : number ) : number {
232- const rangeSize = max - min + 1 ;
233- const offset = this . rnd . nextInt ( rangeSize ) ;
234- return min + offset ;
235- }
236- }
237-
238- class Random {
239- private seed : number ;
240-
241- constructor ( seed : number ) {
242- this . seed = seed ;
243- }
244-
245- nextInt ( max : number ) : number {
246- // Update the seed with pseudo-randomized numbers using a Linear Congruential Generator (LCG).
247- this . seed = ( this . seed * 9301 + 49297 ) % 233280 ;
248- const rnd = this . seed / 233280 ;
249- return Math . floor ( rnd * max ) ;
250- }
251-
252- nextFloat ( ) : number {
253- this . seed = ( this . seed * 9301 + 49297 ) % 233280 ;
254- return this . seed / 233280 ;
255- }
256- }
257-
258- class StringBuilder {
259- private buffer : string [ ] = [ ] ;
260-
261- append ( str : string ) : StringBuilder {
262- this . buffer . push ( str ) ;
263- return this ;
264- }
265-
266- appendCodePoint ( codePoint : number ) : StringBuilder {
267- this . buffer . push ( String . fromCodePoint ( codePoint ) ) ;
268- return this ;
269- }
270-
271- toString ( ) : string {
272- return this . buffer . join ( '' ) ;
273- }
274-
275- length ( ) : number {
276- return this . buffer . join ( '' ) . length ;
277- }
278- }
279- } ) ;
0 commit comments