ronantakizawa/codereview-bench huggingface dataset:
Repository: gofiber/fiber
File: path.go (Go)

Code before:
			break
		}
	}

	return len(s)
}

// GetTrimmedParam trims the ':' & '?' from a string
func GetTrimmedParam(param string) string {
	start := 0
	end := len(param)

	if param[start] != paramStarterChar { // is not a param
		return param
	}
	start++
	if param[end-1] == optionalParam { // is ?
		end--
	}

	return param[start:end]
}

// RemoveEscapeChar remove escape charaters
func RemoveEscapeChar(word string) string {
	if -1 != strings.IndexByte(word, escapeChar) {
		return strings.ReplaceAll(word, string(escapeChar), "")
	}
	return word
}


Review comment (security):
@ReneWerner87 , just asking: don't you think that `strings.IndexByte(word, escapeChar) != -1` is a bit clearer and in sync with "the best practices"?)

Code after:
			break
		}
	}

	return len(s)
}

// GetTrimmedParam trims the ':' & '?' from a string
func GetTrimmedParam(param string) string {
	start := 0
	end := len(param)

	if param[start] != paramStarterChar { // is not a param
		return param
	}
	start++
	if param[end-1] == optionalParam { // is ?
		end--
	}

	return param[start:end]
}

// RemoveEscapeChar remove escape characters
func RemoveEscapeChar(word string) string {
	if strings.IndexByte(word, escapeChar) != -1 {
		return strings.ReplaceAll(word, string(escapeChar), "")
	}
	return word
}
Repository: gofiber/fiber
File: middleware/pprof/pprof.go (Go)

Code before:
			pprofCmdline(c.Context())
		case "/debug/pprof/profile":
			pprofProfile(c.Context())
		case "/debug/pprof/symbol":
			pprofSymbol(c.Context())
		case "/debug/pprof/trace":
			pprofTrace(c.Context())
		case "/debug/pprof/allocs":
			pprofAllocs(c.Context())
		case "/debug/pprof/block":
			pprofBlock(c.Context())
		case "/debug/pprof/goroutine":
			pprofGoroutine(c.Context())
		case "/debug/pprof/heap":
			pprofHeap(c.Context())
		case "/debug/pprof/mutex":
			pprofMutex(c.Context())
		case "/debug/pprof/threadcreate":
			pprofThreadcreate(c.Context())
		default:
			// pprof index only works with trailing slash
			if strings.HasSuffix(path, "/") {
				path = strings.TrimSuffix(path, "/")
			} else {
				path = "/debug/pprof"
			}

			return c.Redirect(path, fiber.StatusFound)
		}
		return nil
	}
}


Review comment (suggestion):
We probably don't need this if statement. If there is no suffix, `TrimSuffix` returns the string unchanged: 

>  TrimSuffix returns s without the provided trailing suffix string. If s doesn't end with suffix, s is returned unchanged. 

Code after:
			pprofCmdline(c.Context())
		case "/debug/pprof/profile":
			pprofProfile(c.Context())
		case "/debug/pprof/symbol":
			pprofSymbol(c.Context())
		case "/debug/pprof/trace":
			pprofTrace(c.Context())
		case "/debug/pprof/allocs":
			pprofAllocs(c.Context())
		case "/debug/pprof/block":
			pprofBlock(c.Context())
		case "/debug/pprof/goroutine":
			pprofGoroutine(c.Context())
		case "/debug/pprof/heap":
			pprofHeap(c.Context())
		case "/debug/pprof/mutex":
			pprofMutex(c.Context())
		case "/debug/pprof/threadcreate":
			pprofThreadcreate(c.Context())
		default:
			// pprof index only works with trailing slash
			if strings.HasSuffix(path, "/") {
				path = strings.TrimRight(path, "/")
			} else {
				path = "/debug/pprof"
			}

			return c.Redirect(path, fiber.StatusFound)
		}
		return nil
	}
}
Repository: gofiber/fiber
File: middleware/encryptcookie/config.go (Go)

Code before:
package encryptcookie

import "github.com/gofiber/fiber/v2"

// Config defines the config for middleware.
type Config struct {
	// Next defines a function to skip this middleware when returned true.
	//
	// Optional. Default: nil
	Next func(c *fiber.Ctx) bool

	// Array of cookies that should not encrypt
	//
	// Optional. Default: []
	Except []string

	// Base64 unique key to encode & decode cookies
	//
	// Optional. Default: Generating new key on every run
	Key string

	// Custom function to encrypt cookies
	//
	// Optional. Default: EncryptCookie
	Encryptor func(decryptedString, key string) (string, error)

	// Custom function to decrypt cookies
	//
	// Optional. Default: DecryptCookie
	Decryptor func(encryptedString, key string) (string, error)
}

// ConfigDefault is the default config
var ConfigDefault = Config{
	Next:      nil,
	Except:    make([]string, 0),
	Key:       GenerateKey(32),

Review comment (suggestion):
```suggestion
	// Array of cookie keys that should not be encrypted
```

Code after:
package encryptcookie

import "github.com/gofiber/fiber/v2"

// Config defines the config for middleware.
type Config struct {
	// Next defines a function to skip this middleware when returned true.
	//
	// Optional. Default: nil
	Next func(c *fiber.Ctx) bool

	// Array of cookie keys that should not be encrypted.
	//
	// Optional. Default: []
	Except []string

	// Base64 encoded unique key to encode & decode cookies.
	//
	// Required. Key length should be 32 characters.
	// You may use `encryptcookie.GenerateKey()` to generate a new key.
	Key string

	// Custom function to encrypt cookies.
	//
	// Optional. Default: EncryptCookie
	Encryptor func(decryptedString, key string) (string, error)

	// Custom function to decrypt cookies.
	//
	// Optional. Default: DecryptCookie
	Decryptor func(encryptedString, key string) (string, error)
}

// ConfigDefault is the default config
var ConfigDefault = Config{
	Next:      nil,
	Except:    make([]string, 0),
Repository: gofiber/fiber
File: middleware/encryptcookie/config.go (Go)

Code before:

	// Override config if provided
	if len(config) > 0 {
		cfg = config[0]

		// Set default values

		if cfg.Next == nil {
			cfg.Next = ConfigDefault.Next
		}

		if cfg.Except == nil {
			cfg.Except = ConfigDefault.Except
		}

		if cfg.Encryptor == nil {
			cfg.Encryptor = ConfigDefault.Encryptor
		}

		if cfg.Decryptor == nil {
			cfg.Decryptor = ConfigDefault.Decryptor
		}
	}

	if cfg.Key == "" {
		panic("Fiber: Encrypt cookie middleware requires key")
	}

	return cfg
}


Review comment (suggestion):
```suggestion
		panic("fiber: encrypt cookie middleware requires key")
```

Code after:
	cfg := ConfigDefault

	// Override config if provided
	if len(config) > 0 {
		cfg = config[0]

		// Set default values

		if cfg.Next == nil {
			cfg.Next = ConfigDefault.Next
		}

		if cfg.Except == nil {
			cfg.Except = ConfigDefault.Except
		}

		if cfg.Encryptor == nil {
			cfg.Encryptor = ConfigDefault.Encryptor
		}

		if cfg.Decryptor == nil {
			cfg.Decryptor = ConfigDefault.Decryptor
		}
	}

	if cfg.Key == "" {
		panic("fiber: encrypt cookie middleware requires key")
	}

	return cfg
}
Repository: gofiber/fiber
File: middleware/encryptcookie/config.go (Go)

Code before:
package encryptcookie

import "github.com/gofiber/fiber/v2"

// Config defines the config for middleware.
type Config struct {
	// Next defines a function to skip this middleware when returned true.
	//
	// Optional. Default: nil
	Next func(c *fiber.Ctx) bool

	// Array of cookie keys that should not be encrypted.
	//
	// Optional. Default: []
	Except []string

	// Base64 encoded unique key to encode & decode cookies.
	//
	// Required. Key length should be 32 characters.
	// you may use `encryptcookie.GenerateKey(32)` to generate a new key.
	Key string

	// Custom function to encrypt cookies.
	//
	// Optional. Default: EncryptCookie
	Encryptor func(decryptedString, key string) (string, error)

	// Custom function to decrypt cookies.
	//
	// Optional. Default: DecryptCookie
	Decryptor func(encryptedString, key string) (string, error)
}

// ConfigDefault is the default config
var ConfigDefault = Config{
	Next:      nil,
	Except:    make([]string, 0),
	Key:       "",
	Encryptor: EncryptCookie,
	Decryptor: DecryptCookie,
}

// Helper function to set default values
func configDefault(config ...Config) Config {
	// Set default config

Review comment (suggestion):
```suggestion
	// You may use `encryptcookie.GenerateKey(32)` to generate a new key.
```

Code after:
package encryptcookie

import "github.com/gofiber/fiber/v2"

// Config defines the config for middleware.
type Config struct {
	// Next defines a function to skip this middleware when returned true.
	//
	// Optional. Default: nil
	Next func(c *fiber.Ctx) bool

	// Array of cookie keys that should not be encrypted.
	//
	// Optional. Default: []
	Except []string

	// Base64 encoded unique key to encode & decode cookies.
	//
	// Required. Key length should be 32 characters.
	// You may use `encryptcookie.GenerateKey()` to generate a new key.
	Key string

	// Custom function to encrypt cookies.
	//
	// Optional. Default: EncryptCookie
	Encryptor func(decryptedString, key string) (string, error)

	// Custom function to decrypt cookies.
	//
	// Optional. Default: DecryptCookie
	Decryptor func(encryptedString, key string) (string, error)
}

// ConfigDefault is the default config
var ConfigDefault = Config{
	Next:      nil,
	Except:    make([]string, 0),
	Key:       "",
	Encryptor: EncryptCookie,
	Decryptor: DecryptCookie,
}

// Helper function to set default values
func configDefault(config ...Config) Config {
	// Set default config
Repository: gofiber/fiber
File: internal/msgp/errors.go (Go)

Code before:
			out += "/"
		}
		out += fmt.Sprintf("%v", cv)
	}
	return out
}

func addCtx(ctx, add string) string {
	if ctx != "" {
		return add + "/" + ctx
	} else {
		return add
	}
}

// errWrapped allows arbitrary errors passed to WrapError to be enhanced with
// context and unwrapped with Cause()
type errWrapped struct {
	cause error
	ctx   string
}

func (e errWrapped) Error() string {
	if e.ctx != "" {
		return fmt.Sprintf("%s at %s", e.cause, e.ctx)
	} else {
		return e.cause.Error()
	}
}

func (e errWrapped) Resumable() bool {
	if e, ok := e.cause.(Error); ok {
		return e.Resumable()
	}
	return resumableDefault
}

// Unwrap returns the cause.
func (e errWrapped) Unwrap() error { return e.cause }

type errShort struct{}

func (e errShort) Error() string   { return "msgp: too few bytes left to read object" }
func (e errShort) Resumable() bool { return false }

type errFatal struct {
	ctx string
}

func (f errFatal) Error() string {
	out := "msgp: fatal decoding error (unreachable code)"

Review comment (suggestion):
Benchmark_Error/strings.Join([]string{e.Error(),_"at",_ctx},_"_")-12
21007089
54.84 ns/op

Benchmark_Error/fmt.Sprintf("%s_at_%s",_e,_ctx)-12
5242245	
209.6 ns/op

Code after:
		if idx > 0 {
			out += "/"
		}
		out += fmt.Sprintf("%v", cv)
	}
	return out
}

func addCtx(ctx, add string) string {
	if ctx != "" {
		return add + "/" + ctx
	} else {
		return add
	}
}

// errWrapped allows arbitrary errors passed to WrapError to be enhanced with
// context and unwrapped with Cause()
type errWrapped struct {
	cause error
	ctx   string
}

func (e errWrapped) Error() string {
	if e.ctx != "" {
		return strings.Join([]string{e.cause.Error(), "at", e.ctx}, " ")
	} else {
		return e.cause.Error()
	}
}

func (e errWrapped) Resumable() bool {
	if e, ok := e.cause.(Error); ok {
		return e.Resumable()
	}
	return resumableDefault
}

// Unwrap returns the cause.
func (e errWrapped) Unwrap() error { return e.cause }

type errShort struct{}

func (e errShort) Error() string   { return "msgp: too few bytes left to read object" }
func (e errShort) Resumable() bool { return false }

type errFatal struct {
	ctx string
}

func (f errFatal) Error() string {
Repository: gofiber/fiber
File: internal/uuid/dce.go (Go)

Code before:
	return NewDCESecurity(Group, uint32(os.Getgid()))
}

// Domain returns the domain for a Version 2 UUID.  Domains are only defined
// for Version 2 UUIDs.
func (uuid UUID) Domain() Domain {
	return Domain(uuid[9])
}

// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
// UUIDs.
func (uuid UUID) ID() uint32 {
	return binary.BigEndian.Uint32(uuid[0:4])
}

func (d Domain) String() string {
	switch d {
	case Person:
		return "Person"
	case Group:
		return "Group"
	case Org:
		return "Org"
	}
	return fmt.Sprintf("Domain%d", int(d))
}


Review comment (suggestion):
Benchmark_Error/fmt.Sprintf("Domain%d",_int(d))-12         	
13134072	        
88.10 ns/op

Benchmark_Error/strings.Join([]string{"Domain",_strconv.Itoa(int(d))},_"")-12         	
35117198	        
43.97 ns/op

Code after:
func NewDCEGroup() (UUID, error) {
	return NewDCESecurity(Group, uint32(os.Getgid()))
}

// Domain returns the domain for a Version 2 UUID.  Domains are only defined
// for Version 2 UUIDs.
func (uuid UUID) Domain() Domain {
	return Domain(uuid[9])
}

// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
// UUIDs.
func (uuid UUID) ID() uint32 {
	return binary.BigEndian.Uint32(uuid[0:4])
}

func (d Domain) String() string {
	switch d {
	case Person:
		return "Person"
	case Group:
		return "Group"
	case Org:
		return "Org"
	}
	return strings.Join([]string{"Domain", strconv.Itoa(int(d))}, "")
}
Repository: gofiber/fiber
File: internal/uuid/marshal.go (Go)

Code before:
	encodeHex(js[:], uuid)
	return js[:], nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
	id, err := ParseBytes(data)
	if err != nil {
		return err
	}
	*uuid = id
	return nil
}

// MarshalBinary implements encoding.BinaryMarshaler.
func (uuid UUID) MarshalBinary() ([]byte, error) {
	return uuid[:], nil
}

// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (uuid *UUID) UnmarshalBinary(data []byte) error {
	if len(data) != 16 {
		return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
	}
	copy(uuid[:], data)
	return nil
}


Review comment (suggestion):
Benchmark_Error/fmt.Errorf("invalid_UUID_(got_%d_bytes)",_len(data))-12         	 
7709733	       
132.9 ns/op

Benchmark_Error/errors.New("invalid_UUID_(got_"_+_strconv.Itoa(len(data))_+_"_bytes)")-12         	
16772258	        
75.60 ns/op

Code after:
// MarshalText implements encoding.TextMarshaler.
func (uuid UUID) MarshalText() ([]byte, error) {
	var js [36]byte
	encodeHex(js[:], uuid)
	return js[:], nil
}

// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
	id, err := ParseBytes(data)
	if err != nil {
		return err
	}
	*uuid = id
	return nil
}

// MarshalBinary implements encoding.BinaryMarshaler.
func (uuid UUID) MarshalBinary() ([]byte, error) {
	return uuid[:], nil
}

// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (uuid *UUID) UnmarshalBinary(data []byte) error {
	if len(data) != 16 {
		return errors.New("invalid UUID (got " + strconv.Itoa(len(data)) + " bytes)")
	}
	copy(uuid[:], data)
	return nil
}
Repository: gofiber/fiber
File: internal/uuid/sql.go (Go)

Code before:
package uuid

import (
	"database/sql/driver"
	"fmt"
)

// Scan implements sql.Scanner so UUIDs can be read from databases transparently
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
	switch src := src.(type) {
	case nil:
		return nil

	case string:
		// if an empty UUID comes from a table, we return a null UUID
		if src == "" {
			return nil
		}

		// see Parse for required string format
		u, err := Parse(src)
		if err != nil {
			return fmt.Errorf("Scan: %v", err)
		}

		*uuid = u

	case []byte:
		// if an empty UUID comes from a table, we return a null UUID
		if len(src) == 0 {
			return nil
		}

		// assumes a simple slice of bytes if 16 bytes
		// otherwise attempts to parse
		if len(src) != 16 {
			return uuid.Scan(string(src))
		}
		copy((*uuid)[:], src)

	default:
		return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
	}

	return nil
}

// Value implements sql.Valuer so that UUIDs can be written to databases
// transparently. Currently, UUIDs map to strings. Please consult

Review comment (suggestion):
Benchmark_Error/fmt.Errorf("Scan:_%v",_err)-12         	 
5615924	       
188.9 ns/op

Benchmark_Error/errors.New("Scan:_"_+_err.Error())-12  	
18481023	        
64.12 ns/op

Code after:
package uuid

import (
	"database/sql/driver"
	"errors"
	"fmt"
)

// Scan implements sql.Scanner so UUIDs can be read from databases transparently
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
	switch src := src.(type) {
	case nil:
		return nil

	case string:
		// if an empty UUID comes from a table, we return a null UUID
		if src == "" {
			return nil
		}

		// see Parse for required string format
		u, err := Parse(src)
		if err != nil {
			return errors.New("Scan: " + err.Error())
		}

		*uuid = u

	case []byte:
		// if an empty UUID comes from a table, we return a null UUID
		if len(src) == 0 {
			return nil
		}

		// assumes a simple slice of bytes if 16 bytes
		// otherwise attempts to parse
		if len(src) != 16 {
			return uuid.Scan(string(src))
		}
		copy((*uuid)[:], src)

	default:
		// here we use %T for type
		return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
	}

	return nil
}
Repository: gofiber/fiber
File: utils/http.go (Go)

Code before:
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
// 🤖 Github Repository: https://github.com/gofiber/fiber
// 📌 API Documentation: https://docs.gofiber.io

package utils

const MIMEOctetStream = "application/octet-stream"

// GetMIME returns the content-type of a file extension
func GetMIME(extension string) (mime string) {
	if extension == "" {
		return mime
	}
	if extension[0] == '.' {
		mime = mimeExtensions[extension[1:]]
	} else {
		mime = mimeExtensions[extension]
	}
	if mime == "" {
		return MIMEOctetStream
	}
	return mime
}

// limits for HTTP statuscodes
const (
	statusMessageMin = 100
	statusMessageMax = 511
)

// StatusMessage returns the correct message for the provided HTTP statuscode
func StatusMessage(status int) string {
	if status < statusMessageMin || status > statusMessageMax {
		return ""
	}
	return statusMessage[status]

Review comment (suggestion):
```go
Benchmark_Compare/""_==_""-12         	1000000000	         0.4273 ns/op
Benchmark_Compare/"test"_==_""-12     	1000000000	         0.3923 ns/op
Benchmark_Compare/len("")_==_0-12     	1000000000	         0.3785 ns/op
Benchmark_Compare/len("test")_==_0-12 	1000000000	         0.3814 ns/op
```
len(x) == 0 is faster than x == ""

Code after:
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
// 🤖 Github Repository: https://github.com/gofiber/fiber
// 📌 API Documentation: https://docs.gofiber.io

package utils

const MIMEOctetStream = "application/octet-stream"

// GetMIME returns the content-type of a file extension
func GetMIME(extension string) (mime string) {
	if len(extension) == 0 {
		return mime
	}
	if extension[0] == '.' {
		mime = mimeExtensions[extension[1:]]
	} else {
		mime = mimeExtensions[extension]
	}
	if len(mime) == 0 {
		return MIMEOctetStream
	}
	return mime
}

// limits for HTTP statuscodes
const (
	statusMessageMin = 100
	statusMessageMax = 511
)

// StatusMessage returns the correct message for the provided HTTP statuscode
func StatusMessage(status int) string {
	if status < statusMessageMin || status > statusMessageMax {
		return ""
	}
	return statusMessage[status]
Repository: gofiber/fiber
File: middleware/session/store.go (Go)

Code before:
	sess := acquireSession()
	sess.ctx = c
	sess.config = s
	sess.id = id
	sess.fresh = fresh

	// Fetch existing data
	if loadData {
		raw, err := s.Storage.Get(id)
		// Unmashal if we found data
		if raw != nil && err == nil {
			mux.Lock()
			defer mux.Unlock()
			_, _ = sess.byteBuffer.Write(raw)
			encCache := gob.NewDecoder(sess.byteBuffer)
			err := encCache.Decode(&sess.data.Data)
			if err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		} else {
			// raw is nil, which means id is not in the storage
			// so it means that id is not valid (mainly because of id is expired or user provides an invalid id)
			// therefore, we regenerate a id
			sess.fresh = true
			sess.id = s.KeyGenerator()
		}
	}

	return sess, nil
}

// getSessionID will return the session id from:
// 1. cookie
// 2. http headers
// 3. query string
func (s *Store) getSessionID(c *fiber.Ctx) string {
	id := c.Cookies(s.sessionName)
	if len(id) > 0 {
		return id
	}

	if s.source == SourceHeader {
		id = string(c.Request().Header.Peek(s.sessionName))
		if len(id) > 0 {
			return id
		}
	}

	if s.source == SourceURLQuery {

Review comment (suggestion):
can you create a method that combines the two commands and use it in sess.Regenerate and store, think that would be even cleaner

```go
func (s *Session) refresh() {
    s.id = s.config.KeyGenerator()
    s.fresh = true
}
```

Code after:
	sess := acquireSession()
	sess.ctx = c
	sess.config = s
	sess.id = id
	sess.fresh = fresh

	// Fetch existing data
	if loadData {
		raw, err := s.Storage.Get(id)
		// Unmashal if we found data
		if raw != nil && err == nil {
			mux.Lock()
			defer mux.Unlock()
			_, _ = sess.byteBuffer.Write(raw)
			encCache := gob.NewDecoder(sess.byteBuffer)
			err := encCache.Decode(&sess.data.Data)
			if err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		} else {
			// raw is nil, which means id is not in the storage
			// so it means that id is not valid (mainly because of id is expired or user provides an invalid id)
			// therefore, we regenerate a id
			sess.refresh()
		}
	}

	return sess, nil
}

// getSessionID will return the session id from:
// 1. cookie
// 2. http headers
// 3. query string
func (s *Store) getSessionID(c *fiber.Ctx) string {
	id := c.Cookies(s.sessionName)
	if len(id) > 0 {
		return id
	}

	if s.source == SourceHeader {
		id = string(c.Request().Header.Peek(s.sessionName))
		if len(id) > 0 {
			return id
		}
	}

	if s.source == SourceURLQuery {
		id = c.Query(s.sessionName)
Repository: gofiber/fiber
File: middleware/session/session.go (Go)

Code before:
	if s.data == nil {
		return nil
	}

	// Reset local data
	s.data.Reset()

	// Use external Storage if exist
	if err := s.config.Storage.Delete(s.id); err != nil {
		return err
	}

	// Expire session
	s.delSession()
	return nil
}

// Regenerate generates a new session id and delete the old one from Storage
func (s *Session) Regenerate() error {

	// Delete old id from storage
	if err := s.config.Storage.Delete(s.id); err != nil {
		return err
	}

	// generate a new session, and set session.fresh to be true
	s.refresh()

	return nil
}

// refresh generates a new session, and set session.fresh to be true
func (s *Session) refresh() {
	// Create a new id
	s.id = s.config.KeyGenerator()

	// We assign a new id to the session, so the session must be fresh
	s.fresh = true
}

// Save will update the storage and client cookie
func (s *Session) Save() error {

	// Better safe than sorry
	if s.data == nil {
		return nil
	}

	// Check if session has your own expiration, otherwise use default value
	if s.exp <= 0 {
		s.exp = s.config.Expiration

Review comment (suggestion):
```suggestion
	// Generate a new session, and set session.fresh to true
```

Code after:
	if s.data == nil {
		return nil
	}

	// Reset local data
	s.data.Reset()

	// Use external Storage if exist
	if err := s.config.Storage.Delete(s.id); err != nil {
		return err
	}

	// Expire session
	s.delSession()
	return nil
}

// Regenerate generates a new session id and delete the old one from Storage
func (s *Session) Regenerate() error {

	// Delete old id from storage
	if err := s.config.Storage.Delete(s.id); err != nil {
		return err
	}

	// Generate a new session, and set session.fresh to true
	s.refresh()

	return nil
}

// refresh generates a new session, and set session.fresh to be true
func (s *Session) refresh() {
	// Create a new id
	s.id = s.config.KeyGenerator()

	// We assign a new id to the session, so the session must be fresh
	s.fresh = true
}

// Save will update the storage and client cookie
func (s *Session) Save() error {

	// Better safe than sorry
	if s.data == nil {
		return nil
	}

	// Check if session has your own expiration, otherwise use default value
	if s.exp <= 0 {
		s.exp = s.config.Expiration