194 lines
4.0 KiB
Go
194 lines
4.0 KiB
Go
|
package httphead
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"sort"
|
||
|
)
|
||
|
|
||
|
// Option represents a header option.
|
||
|
type Option struct {
|
||
|
Name []byte
|
||
|
Parameters Parameters
|
||
|
}
|
||
|
|
||
|
// Size returns number of bytes need to be allocated for use in opt.Copy.
|
||
|
func (opt Option) Size() int {
|
||
|
return len(opt.Name) + opt.Parameters.bytes
|
||
|
}
|
||
|
|
||
|
// Copy copies all underlying []byte slices into p and returns new Option.
|
||
|
// Note that p must be at least of opt.Size() length.
|
||
|
func (opt Option) Copy(p []byte) Option {
|
||
|
n := copy(p, opt.Name)
|
||
|
opt.Name = p[:n]
|
||
|
opt.Parameters, p = opt.Parameters.Copy(p[n:])
|
||
|
return opt
|
||
|
}
|
||
|
|
||
|
// Clone is a shorthand for making slice of opt.Size() sequenced with Copy()
|
||
|
// call.
|
||
|
func (opt Option) Clone() Option {
|
||
|
return opt.Copy(make([]byte, opt.Size()))
|
||
|
}
|
||
|
|
||
|
// String represents option as a string.
|
||
|
func (opt Option) String() string {
|
||
|
return "{" + string(opt.Name) + " " + opt.Parameters.String() + "}"
|
||
|
}
|
||
|
|
||
|
// NewOption creates named option with given parameters.
|
||
|
func NewOption(name string, params map[string]string) Option {
|
||
|
p := Parameters{}
|
||
|
for k, v := range params {
|
||
|
p.Set([]byte(k), []byte(v))
|
||
|
}
|
||
|
return Option{
|
||
|
Name: []byte(name),
|
||
|
Parameters: p,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Equal reports whether option is equal to b.
|
||
|
func (opt Option) Equal(b Option) bool {
|
||
|
if bytes.Equal(opt.Name, b.Name) {
|
||
|
return opt.Parameters.Equal(b.Parameters)
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// Parameters represents option's parameters.
|
||
|
type Parameters struct {
|
||
|
pos int
|
||
|
bytes int
|
||
|
arr [8]pair
|
||
|
dyn []pair
|
||
|
}
|
||
|
|
||
|
// Equal reports whether a equal to b.
|
||
|
func (p Parameters) Equal(b Parameters) bool {
|
||
|
switch {
|
||
|
case p.dyn == nil && b.dyn == nil:
|
||
|
case p.dyn != nil && b.dyn != nil:
|
||
|
default:
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
ad, bd := p.data(), b.data()
|
||
|
if len(ad) != len(bd) {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
sort.Sort(pairs(ad))
|
||
|
sort.Sort(pairs(bd))
|
||
|
|
||
|
for i := 0; i < len(ad); i++ {
|
||
|
av, bv := ad[i], bd[i]
|
||
|
if !bytes.Equal(av.key, bv.key) || !bytes.Equal(av.value, bv.value) {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
// Size returns number of bytes that needed to copy p.
|
||
|
func (p *Parameters) Size() int {
|
||
|
return p.bytes
|
||
|
}
|
||
|
|
||
|
// Copy copies all underlying []byte slices into dst and returns new
|
||
|
// Parameters.
|
||
|
// Note that dst must be at least of p.Size() length.
|
||
|
func (p *Parameters) Copy(dst []byte) (Parameters, []byte) {
|
||
|
ret := Parameters{
|
||
|
pos: p.pos,
|
||
|
bytes: p.bytes,
|
||
|
}
|
||
|
if p.dyn != nil {
|
||
|
ret.dyn = make([]pair, len(p.dyn))
|
||
|
for i, v := range p.dyn {
|
||
|
ret.dyn[i], dst = v.copy(dst)
|
||
|
}
|
||
|
} else {
|
||
|
for i, p := range p.arr {
|
||
|
ret.arr[i], dst = p.copy(dst)
|
||
|
}
|
||
|
}
|
||
|
return ret, dst
|
||
|
}
|
||
|
|
||
|
// Get returns value by key and flag about existence such value.
|
||
|
func (p *Parameters) Get(key string) (value []byte, ok bool) {
|
||
|
for _, v := range p.data() {
|
||
|
if string(v.key) == key {
|
||
|
return v.value, true
|
||
|
}
|
||
|
}
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
// Set sets value by key.
|
||
|
func (p *Parameters) Set(key, value []byte) {
|
||
|
p.bytes += len(key) + len(value)
|
||
|
|
||
|
if p.pos < len(p.arr) {
|
||
|
p.arr[p.pos] = pair{key, value}
|
||
|
p.pos++
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if p.dyn == nil {
|
||
|
p.dyn = make([]pair, len(p.arr), len(p.arr)+1)
|
||
|
copy(p.dyn, p.arr[:])
|
||
|
}
|
||
|
p.dyn = append(p.dyn, pair{key, value})
|
||
|
}
|
||
|
|
||
|
// ForEach iterates over parameters key-value pairs and calls cb for each one.
|
||
|
func (p *Parameters) ForEach(cb func(k, v []byte) bool) {
|
||
|
for _, v := range p.data() {
|
||
|
if !cb(v.key, v.value) {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String represents parameters as a string.
|
||
|
func (p *Parameters) String() (ret string) {
|
||
|
ret = "["
|
||
|
for i, v := range p.data() {
|
||
|
if i > 0 {
|
||
|
ret += " "
|
||
|
}
|
||
|
ret += string(v.key) + ":" + string(v.value)
|
||
|
}
|
||
|
return ret + "]"
|
||
|
}
|
||
|
|
||
|
func (p *Parameters) data() []pair {
|
||
|
if p.dyn != nil {
|
||
|
return p.dyn
|
||
|
}
|
||
|
return p.arr[:p.pos]
|
||
|
}
|
||
|
|
||
|
type pair struct {
|
||
|
key, value []byte
|
||
|
}
|
||
|
|
||
|
func (p pair) copy(dst []byte) (pair, []byte) {
|
||
|
n := copy(dst, p.key)
|
||
|
p.key = dst[:n]
|
||
|
m := n + copy(dst[n:], p.value)
|
||
|
p.value = dst[n:m]
|
||
|
|
||
|
dst = dst[m:]
|
||
|
|
||
|
return p, dst
|
||
|
}
|
||
|
|
||
|
type pairs []pair
|
||
|
|
||
|
func (p pairs) Len() int { return len(p) }
|
||
|
func (p pairs) Less(a, b int) bool { return bytes.Compare(p[a].key, p[b].key) == -1 }
|
||
|
func (p pairs) Swap(a, b int) { p[a], p[b] = p[b], p[a] }
|