@@ -78,13 +78,14 @@ QueryString.unescapeBuffer = function(s, decodeSpaces) {
78
78
} ;
79
79
80
80
81
- QueryString . unescape = function ( s , decodeSpaces ) {
81
+ function qsUnescape ( s , decodeSpaces ) {
82
82
try {
83
83
return decodeURIComponent ( s ) ;
84
84
} catch ( e ) {
85
85
return QueryString . unescapeBuffer ( s , decodeSpaces ) . toString ( ) ;
86
86
}
87
- } ;
87
+ }
88
+ QueryString . unescape = qsUnescape ;
88
89
89
90
90
91
var hexTable = new Array ( 256 ) ;
@@ -198,87 +199,190 @@ QueryString.stringify = QueryString.encode = function(obj, sep, eq, options) {
198
199
return '' ;
199
200
} ;
200
201
201
- // Parse a key= val string.
202
+ // Parse a key/ val string.
202
203
QueryString . parse = QueryString . decode = function ( qs , sep , eq , options ) {
203
204
sep = sep || '&' ;
204
205
eq = eq || '=' ;
205
- const eqLen = eq . length ;
206
- var obj = { } ;
206
+
207
+ const obj = { } ;
207
208
208
209
if ( typeof qs !== 'string' || qs . length === 0 ) {
209
210
return obj ;
210
211
}
211
212
213
+ if ( typeof sep !== 'string' )
214
+ sep += '' ;
215
+
216
+ const eqLen = eq . length ;
217
+ const sepLen = sep . length ;
218
+
212
219
var maxKeys = 1000 ;
213
220
if ( options && typeof options . maxKeys === 'number' ) {
214
221
maxKeys = options . maxKeys ;
215
222
}
216
223
217
- // maxKeys <= 0 means that we should not limit keys count
218
- if ( maxKeys > 0 && isFinite ( maxKeys ) ) {
219
- qs = qs . split ( sep , maxKeys ) ;
220
- } else {
221
- qs = qs . split ( sep ) ;
222
- }
223
-
224
- var len = qs . length ;
224
+ var pairs = Infinity ;
225
+ if ( maxKeys > 0 )
226
+ pairs = maxKeys ;
225
227
226
228
var decode = QueryString . unescape ;
227
229
if ( options && typeof options . decodeURIComponent === 'function' ) {
228
230
decode = options . decodeURIComponent ;
229
231
}
230
-
231
- var keys = [ ] ;
232
- for ( var i = 0 ; i < len ; ++ i ) {
233
- // replacePlus() is used instead of a regexp because it is ~15-30% faster
234
- // with v8 4.7
235
- const x = replacePlus ( qs [ i ] ) ;
236
- const idx = x . indexOf ( eq ) ;
237
- var k , v ;
238
-
239
- if ( idx >= 0 ) {
240
- k = decodeStr ( x . substring ( 0 , idx ) , decode ) ;
241
- v = decodeStr ( x . substring ( idx + eqLen ) , decode ) ;
232
+ const customDecode = ( decode !== qsUnescape ) ;
233
+
234
+ const keys = [ ] ;
235
+ var lastPos = 0 ;
236
+ var sepIdx = 0 ;
237
+ var eqIdx = 0 ;
238
+ var key = '' ;
239
+ var value = '' ;
240
+ var keyEncoded = customDecode ;
241
+ var valEncoded = customDecode ;
242
+ var encodeCheck = 0 ;
243
+ for ( var i = 0 ; i < qs . length ; ++ i ) {
244
+ const code = qs . charCodeAt ( i ) ;
245
+
246
+ // Try matching key/value pair separator (e.g. '&')
247
+ if ( code === sep . charCodeAt ( sepIdx ) ) {
248
+ if ( ++ sepIdx === sepLen ) {
249
+ // Key/value pair separator match!
250
+ const end = i - sepIdx + 1 ;
251
+ if ( eqIdx < eqLen ) {
252
+ // If we didn't find the key/value separator, treat the substring as
253
+ // part of the key instead of the value
254
+ if ( lastPos < end )
255
+ key += qs . slice ( lastPos , end ) ;
256
+ } else if ( lastPos < end )
257
+ value += qs . slice ( lastPos , end ) ;
258
+ if ( keyEncoded )
259
+ key = decodeStr ( key , decode ) ;
260
+ if ( valEncoded )
261
+ value = decodeStr ( value , decode ) ;
262
+ // Use a key array lookup instead of using hasOwnProperty(), which is
263
+ // slower
264
+ if ( keys . indexOf ( key ) === - 1 ) {
265
+ obj [ key ] = value ;
266
+ keys [ keys . length ] = key ;
267
+ } else {
268
+ const curValue = obj [ key ] ;
269
+ // `instanceof Array` is used instead of Array.isArray() because it
270
+ // is ~15-20% faster with v8 4.7 and is safe to use because we are
271
+ // using it with values being created within this function
272
+ if ( curValue instanceof Array )
273
+ curValue [ curValue . length ] = value ;
274
+ else
275
+ obj [ key ] = [ curValue , value ] ;
276
+ }
277
+ if ( -- pairs === 0 )
278
+ break ;
279
+ keyEncoded = valEncoded = customDecode ;
280
+ encodeCheck = 0 ;
281
+ key = value = '' ;
282
+ lastPos = i + 1 ;
283
+ sepIdx = eqIdx = 0 ;
284
+ }
285
+ continue ;
242
286
} else {
243
- k = decodeStr ( x , decode ) ;
244
- v = '' ;
287
+ sepIdx = 0 ;
288
+ if ( ! valEncoded ) {
289
+ // Try to match an (valid) encoded byte (once) to minimize unnecessary
290
+ // calls to string decoding functions
291
+ if ( code === 37 /*%*/ ) {
292
+ encodeCheck = 1 ;
293
+ } else if ( encodeCheck > 0 &&
294
+ ( ( code >= 48 /*0*/ && code <= 57 /*9*/ ) ||
295
+ ( code >= 65 /*A*/ && code <= 70 /*Z*/ ) ||
296
+ ( code >= 97 /*a*/ && code <= 102 /*z*/ ) ) ) {
297
+ if ( ++ encodeCheck === 3 )
298
+ valEncoded = true ;
299
+ } else {
300
+ encodeCheck = 0 ;
301
+ }
302
+ }
245
303
}
246
304
247
- // Use a key array lookup instead of using hasOwnProperty(), which is slower
248
- if ( keys . indexOf ( k ) === - 1 ) {
249
- obj [ k ] = v ;
250
- keys . push ( k ) ;
251
- } else if ( obj [ k ] instanceof Array ) {
252
- // `instanceof Array` is used instead of Array.isArray() because it is
253
- // ~15-20% faster with v8 4.7 and is safe to use because we are using it
254
- // with values being created within this function
255
- obj [ k ] . push ( v ) ;
305
+ // Try matching key/value separator (e.g. '=') if we haven't already
306
+ if ( eqIdx < eqLen ) {
307
+ if ( code === eq . charCodeAt ( eqIdx ) ) {
308
+ if ( ++ eqIdx === eqLen ) {
309
+ // Key/value separator match!
310
+ const end = i - eqIdx + 1 ;
311
+ if ( lastPos < end )
312
+ key += qs . slice ( lastPos , end ) ;
313
+ encodeCheck = 0 ;
314
+ lastPos = i + 1 ;
315
+ }
316
+ continue ;
317
+ } else {
318
+ eqIdx = 0 ;
319
+ if ( ! keyEncoded ) {
320
+ // Try to match an (valid) encoded byte once to minimize unnecessary
321
+ // calls to string decoding functions
322
+ if ( code === 37 /*%*/ ) {
323
+ encodeCheck = 1 ;
324
+ } else if ( encodeCheck > 0 &&
325
+ ( ( code >= 48 /*0*/ && code <= 57 /*9*/ ) ||
326
+ ( code >= 65 /*A*/ && code <= 70 /*Z*/ ) ||
327
+ ( code >= 97 /*a*/ && code <= 102 /*z*/ ) ) ) {
328
+ if ( ++ encodeCheck === 3 )
329
+ keyEncoded = true ;
330
+ } else {
331
+ encodeCheck = 0 ;
332
+ }
333
+ }
334
+ }
335
+ }
336
+
337
+ if ( code === 43 /*+*/ ) {
338
+ if ( eqIdx < eqLen ) {
339
+ if ( i - lastPos > 0 )
340
+ key += qs . slice ( lastPos , i ) ;
341
+ key += '%20' ;
342
+ keyEncoded = true ;
343
+ } else {
344
+ if ( i - lastPos > 0 )
345
+ value += qs . slice ( lastPos , i ) ;
346
+ value += '%20' ;
347
+ valEncoded = true ;
348
+ }
349
+ lastPos = i + 1 ;
350
+ }
351
+ }
352
+
353
+ // Check if we have leftover key or value data
354
+ if ( pairs > 0 && ( lastPos < qs . length || eqIdx > 0 ) ) {
355
+ if ( lastPos < qs . length ) {
356
+ if ( eqIdx < eqLen )
357
+ key += qs . slice ( lastPos ) ;
358
+ else if ( sepIdx < sepLen )
359
+ value += qs . slice ( lastPos ) ;
360
+ }
361
+ if ( keyEncoded )
362
+ key = decodeStr ( key , decode ) ;
363
+ if ( valEncoded )
364
+ value = decodeStr ( value , decode ) ;
365
+ // Use a key array lookup instead of using hasOwnProperty(), which is
366
+ // slower
367
+ if ( keys . indexOf ( key ) === - 1 ) {
368
+ obj [ key ] = value ;
369
+ keys [ keys . length ] = key ;
256
370
} else {
257
- obj [ k ] = [ obj [ k ] , v ] ;
371
+ const curValue = obj [ key ] ;
372
+ // `instanceof Array` is used instead of Array.isArray() because it
373
+ // is ~15-20% faster with v8 4.7 and is safe to use because we are
374
+ // using it with values being created within this function
375
+ if ( curValue instanceof Array )
376
+ curValue [ curValue . length ] = value ;
377
+ else
378
+ obj [ key ] = [ curValue , value ] ;
258
379
}
259
380
}
260
381
261
382
return obj ;
262
383
} ;
263
384
264
385
265
- function replacePlus ( str ) {
266
- var ret = '' ;
267
- var start = 0 ;
268
- var i = - 1 ;
269
- while ( ( i = str . indexOf ( '+' , i + 1 ) ) !== - 1 ) {
270
- ret += str . slice ( start , i ) ;
271
- ret += '%20' ;
272
- start = i + 1 ;
273
- }
274
- if ( start === 0 )
275
- return str ;
276
- if ( start < str . length )
277
- ret += str . slice ( start ) ;
278
- return ret ;
279
- }
280
-
281
-
282
386
// v8 does not optimize functions with try-catch blocks, so we isolate them here
283
387
// to minimize the damage
284
388
function decodeStr ( s , decoder ) {
0 commit comments