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