Skip to content

Commit

Permalink
Reduce the size of the Cookie by 32 bytes. (#1866)
Browse files Browse the repository at this point in the history
Replace the field `bufKV` with two `[]byte` fields and remove the filed `buf`.

- Reduce Cookie from **224** to **192** bytes.
- In the benchmark tests, although there is no significant performance improvement, it theoretically reduces memory usage by **14.3%**.
  • Loading branch information
ksw2000 authored Sep 21, 2024
1 parent 0128871 commit 318e68e
Showing 1 changed file with 45 additions and 46 deletions.
91 changes: 45 additions & 46 deletions cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ type Cookie struct {
domain []byte
path []byte

buf []byte
bufK []byte
bufV []byte

bufKV argsKV
maxAge int

sameSite CookieSameSite
Expand Down Expand Up @@ -156,14 +156,14 @@ func (c *Cookie) Path() []byte {

// SetPath sets cookie path.
func (c *Cookie) SetPath(path string) {
c.buf = append(c.buf[:0], path...)
c.path = normalizePath(c.path, c.buf)
c.bufK = append(c.bufK[:0], path...)
c.path = normalizePath(c.path, c.bufK)
}

// SetPathBytes sets cookie path.
func (c *Cookie) SetPathBytes(path []byte) {
c.buf = append(c.buf[:0], path...)
c.path = normalizePath(c.path, c.buf)
c.bufK = append(c.bufK[:0], path...)
c.path = normalizePath(c.path, c.bufK)
}

// Domain returns cookie domain.
Expand Down Expand Up @@ -284,11 +284,11 @@ func (c *Cookie) AppendBytes(dst []byte) []byte {
dst = append(dst, '=')
dst = AppendUint(dst, c.maxAge)
} else if !c.expire.IsZero() {
c.bufKV.value = AppendHTTPDate(c.bufKV.value[:0], c.expire)
c.bufV = AppendHTTPDate(c.bufV[:0], c.expire)
dst = append(dst, ';', ' ')
dst = append(dst, strCookieExpires...)
dst = append(dst, '=')
dst = append(dst, c.bufKV.value...)
dst = append(dst, c.bufV...)
}
if len(c.domain) > 0 {
dst = appendCookiePart(dst, strCookieDomain, c.domain)
Expand Down Expand Up @@ -336,8 +336,8 @@ func (c *Cookie) AppendBytes(dst []byte) []byte {
// The returned value is valid until the Cookie reused or released (ReleaseCookie).
// Do not store references to the returned value. Make copies instead.
func (c *Cookie) Cookie() []byte {
c.buf = c.AppendBytes(c.buf[:0])
return c.buf
c.bufK = c.AppendBytes(c.bufK[:0])
return c.bufK
}

// String returns cookie representation.
Expand All @@ -357,8 +357,8 @@ var errNoCookies = errors.New("no cookies found")

// Parse parses Set-Cookie header.
func (c *Cookie) Parse(src string) error {
c.buf = append(c.buf[:0], src...)
return c.ParseBytes(c.buf)
c.bufK = append(c.bufK[:0], src...)
return c.ParseBytes(c.bufK)
}

// ParseBytes parses Set-Cookie header.
Expand All @@ -368,30 +368,29 @@ func (c *Cookie) ParseBytes(src []byte) error {
var s cookieScanner
s.b = src

kv := &c.bufKV
if !s.next(kv) {
if !s.next(&c.bufK, &c.bufV) {
return errNoCookies
}

c.key = append(c.key, kv.key...)
c.value = append(c.value, kv.value...)
c.key = append(c.key, c.bufK...)
c.value = append(c.value, c.bufV...)

for s.next(kv) {
if len(kv.key) != 0 {
for s.next(&c.bufK, &c.bufV) {
if len(c.bufK) != 0 {
// Case insensitive switch on first char
switch kv.key[0] | 0x20 {
switch c.bufK[0] | 0x20 {
case 'm':
if caseInsensitiveCompare(strCookieMaxAge, kv.key) {
maxAge, err := ParseUint(kv.value)
if caseInsensitiveCompare(strCookieMaxAge, c.bufK) {
maxAge, err := ParseUint(c.bufV)
if err != nil {
return err
}
c.maxAge = maxAge
}

case 'e': // "expires"
if caseInsensitiveCompare(strCookieExpires, kv.key) {
v := b2s(kv.value)
if caseInsensitiveCompare(strCookieExpires, c.bufK) {
v := b2s(c.bufV)
// Try the same two formats as net/http
// See: https://github.com/golang/go/blob/00379be17e63a5b75b3237819392d2dc3b313a27/src/net/http/cookie.go#L133-L135
exptime, err := time.ParseInLocation(time.RFC1123, v, time.UTC)
Expand All @@ -405,52 +404,52 @@ func (c *Cookie) ParseBytes(src []byte) error {
}

case 'd': // "domain"
if caseInsensitiveCompare(strCookieDomain, kv.key) {
c.domain = append(c.domain, kv.value...)
if caseInsensitiveCompare(strCookieDomain, c.bufK) {
c.domain = append(c.domain, c.bufV...)
}

case 'p': // "path"
if caseInsensitiveCompare(strCookiePath, kv.key) {
c.path = append(c.path, kv.value...)
if caseInsensitiveCompare(strCookiePath, c.bufK) {
c.path = append(c.path, c.bufV...)
}

case 's': // "samesite"
if caseInsensitiveCompare(strCookieSameSite, kv.key) {
if len(kv.value) > 0 {
if caseInsensitiveCompare(strCookieSameSite, c.bufK) {
if len(c.bufV) > 0 {
// Case insensitive switch on first char
switch kv.value[0] | 0x20 {
switch c.bufV[0] | 0x20 {
case 'l': // "lax"
if caseInsensitiveCompare(strCookieSameSiteLax, kv.value) {
if caseInsensitiveCompare(strCookieSameSiteLax, c.bufV) {
c.sameSite = CookieSameSiteLaxMode
}
case 's': // "strict"
if caseInsensitiveCompare(strCookieSameSiteStrict, kv.value) {
if caseInsensitiveCompare(strCookieSameSiteStrict, c.bufV) {
c.sameSite = CookieSameSiteStrictMode
}
case 'n': // "none"
if caseInsensitiveCompare(strCookieSameSiteNone, kv.value) {
if caseInsensitiveCompare(strCookieSameSiteNone, c.bufV) {
c.sameSite = CookieSameSiteNoneMode
}
}
}
}
}
} else if len(kv.value) != 0 {
} else if len(c.bufV) != 0 {
// Case insensitive switch on first char
switch kv.value[0] | 0x20 {
switch c.bufV[0] | 0x20 {
case 'h': // "httponly"
if caseInsensitiveCompare(strCookieHTTPOnly, kv.value) {
if caseInsensitiveCompare(strCookieHTTPOnly, c.bufV) {
c.httpOnly = true
}

case 's': // "secure"
if caseInsensitiveCompare(strCookieSecure, kv.value) {
if caseInsensitiveCompare(strCookieSecure, c.bufV) {
c.secure = true
} else if caseInsensitiveCompare(strCookieSameSite, kv.value) {
} else if caseInsensitiveCompare(strCookieSameSite, c.bufV) {
c.sameSite = CookieSameSiteDefaultMode
}
case 'p': // "partitioned"
if caseInsensitiveCompare(strCookiePartitioned, kv.value) {
if caseInsensitiveCompare(strCookiePartitioned, c.bufV) {
c.partitioned = true
}
}
Expand Down Expand Up @@ -507,7 +506,7 @@ func parseRequestCookies(cookies []argsKV, src []byte) []argsKV {
s.b = src
var kv *argsKV
cookies, kv = allocArg(cookies)
for s.next(kv) {
for s.next(&kv.key, &kv.value) {
if len(kv.key) > 0 || len(kv.value) > 0 {
cookies, kv = allocArg(cookies)
}
Expand All @@ -519,7 +518,7 @@ type cookieScanner struct {
b []byte
}

func (s *cookieScanner) next(kv *argsKV) bool {
func (s *cookieScanner) next(key, val *[]byte) bool {
b := s.b
if len(b) == 0 {
return false
Expand All @@ -532,23 +531,23 @@ func (s *cookieScanner) next(kv *argsKV) bool {
case '=':
if isKey {
isKey = false
kv.key = decodeCookieArg(kv.key, b[:i], false)
*key = decodeCookieArg(*key, b[:i], false)
k = i + 1
}
case ';':
if isKey {
kv.key = kv.key[:0]
*key = (*key)[:0]
}
kv.value = decodeCookieArg(kv.value, b[k:i], true)
*val = decodeCookieArg(*val, b[k:i], true)
s.b = b[i+1:]
return true
}
}

if isKey {
kv.key = kv.key[:0]
*key = (*key)[:0]
}
kv.value = decodeCookieArg(kv.value, b[k:], true)
*val = decodeCookieArg(*val, b[k:], true)
s.b = b[len(b):]
return true
}
Expand Down

0 comments on commit 318e68e

Please sign in to comment.