Skip to content

Commit

Permalink
Iterate whitespace for perf (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
blakeembrey authored Oct 2, 2024
1 parent 927d48a commit 47917c9
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 16 deletions.
55 changes: 40 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,40 +95,49 @@ function parse(str, options) {
throw new TypeError('argument str must be a string');
}

var obj = {}
var obj = {};
var opt = options || {};
var dec = opt.decode || decode;

var index = 0
while (index < str.length) {
var eqIdx = str.indexOf('=', index)
var index = 0;
var eqIdx = 0;
var endIdx = 0;
var len = str.length;
var max = len - 2;

while (index < max) {
eqIdx = str.indexOf('=', index);

// no more cookie pairs
if (eqIdx === -1) {
break
break;
}

var endIdx = str.indexOf(';', index)
endIdx = str.indexOf(';', index);

if (endIdx === -1) {
endIdx = str.length
} else if (endIdx < eqIdx) {
endIdx = len;
} else if (eqIdx > endIdx) {
// backtrack on prior semicolon
index = str.lastIndexOf(';', eqIdx - 1) + 1
continue
index = str.lastIndexOf(';', eqIdx - 1) + 1;
continue;
}

var key = str.slice(index, eqIdx).trim()
var keyStartIdx = startIndex(str, index, eqIdx);
var keyEndIdx = endIndex(str, eqIdx, keyStartIdx);
var key = str.slice(keyStartIdx, keyEndIdx);

// only assign once
if (undefined === obj[key]) {
var val = str.slice(eqIdx + 1, endIdx).trim()
var valStartIdx = startIndex(str, eqIdx + 1, endIdx);
var valEndIdx = endIndex(str, endIdx, valStartIdx);

// quoted values
if (val.charCodeAt(0) === 0x22) {
val = val.slice(1, -1)
if (str.charCodeAt(valStartIdx) === 0x22 /* " */ && str.charCodeAt(valEndIdx - 1) === 0x22 /* " */) {
valStartIdx++;
valEndIdx--;
}

var val = str.slice(valStartIdx, valEndIdx);
obj[key] = tryDecode(val, dec);
}

Expand All @@ -138,6 +147,22 @@ function parse(str, options) {
return obj;
}

function startIndex(str, index, max) {
do {
var code = str.charCodeAt(index);
if (code !== 0x20 /* */ && code !== 0x09 /* \t */) return index;
} while (++index < max);
return max;
}

function endIndex(str, index, min) {
while (index > min) {
var code = str.charCodeAt(--index);
if (code !== 0x20 /* */ && code !== 0x09 /* \t */) return index + 1;
}
return min;
}

/**
* Serialize data into a cookie header.
*
Expand Down
17 changes: 16 additions & 1 deletion test/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('cookie.parse(str)', function () {
})

it('should parse cookie with empty value', function () {
assert.deepEqual(cookie.parse('foo= ; bar='), { foo: '', bar: '' })
assert.deepEqual(cookie.parse('foo=; bar='), { foo: '', bar: '' })
})

it('should URL-decode values', function () {
Expand All @@ -34,6 +34,21 @@ describe('cookie.parse(str)', function () {
assert.deepEqual(cookie.parse('email=%20%22%2c%3b%2f'), { email: ' ",;/' })
})

it('should parse quoted values', function () {
assert.deepEqual(cookie.parse('foo="bar"'), { foo: 'bar' })
assert.deepEqual(cookie.parse('foo=" a b c "'), { foo: ' a b c ' })
})

it('should trim whitespace around key and value', function () {
assert.deepEqual(cookie.parse(' foo = "bar" '), { foo: 'bar' })
assert.deepEqual(cookie.parse(' foo = bar ; fizz = buzz '), { foo: 'bar', fizz: 'buzz' })
assert.deepEqual(cookie.parse(' foo = " a b c " '), { foo: ' a b c ' })
assert.deepEqual(cookie.parse(' = bar '), { '': 'bar' })
assert.deepEqual(cookie.parse(' foo = '), { foo: '' })
assert.deepEqual(cookie.parse(' = '), { '': '' })
assert.deepEqual(cookie.parse('\tfoo\t=\tbar\t'), { foo: 'bar' })
})

it('should return original value on escape error', function () {
assert.deepEqual(cookie.parse('foo=%1;bar=bar'), { foo: '%1', bar: 'bar' })
})
Expand Down

0 comments on commit 47917c9

Please sign in to comment.