Files
fasthttp/args.go
T
2015-10-19 20:55:49 +03:00

317 lines
5.4 KiB
Go

package fasthttp
import (
"bytes"
"errors"
)
type Args struct {
args []argsKV
bufKV argsKV
buf []byte
}
type argsKV struct {
key []byte
value []byte
}
func (a *Args) Clear() {
a.args = a.args[:0]
}
func (a *Args) Len() int {
return len(a.args)
}
func (a *Args) Parse(s string) {
a.buf = AppendBytesStr(a.buf[:0], s)
a.ParseBytes(a.buf)
}
func (a *Args) ParseBytes(b []byte) {
a.Clear()
var p argsParser
p.Init(b)
n := cap(a.args)
a.args = a.args[:n]
for i := 0; i < n; i++ {
kv := &a.args[i]
if !p.Next(kv) {
for j := 0; j < i; j++ {
}
a.args = a.args[:i]
return
}
}
for {
var kv argsKV
if !p.Next(&kv) {
return
}
a.args = append(a.args, kv)
}
}
func (a *Args) String() string {
a.buf = a.AppendBytes(a.buf[:0])
return string(a.buf)
}
func (a *Args) AppendBytes(dst []byte) []byte {
for i, n := 0, len(a.args); i < n; i++ {
kv := &a.args[i]
dst = appendQuotedArg(dst, kv.key)
if len(kv.value) > 0 {
dst = append(dst, '=')
dst = appendQuotedArg(dst, kv.value)
}
if i+1 < n {
dst = append(dst, '&')
}
}
return dst
}
func (a *Args) Del(key string) {
for i, n := 0, len(a.args); i < n; i++ {
kv := &a.args[i]
if EqualBytesStr(kv.key, key) {
tmp := *kv
copy(a.args[i:], a.args[i+1:])
a.args[n-1] = tmp
a.args = a.args[:n-1]
return
}
}
}
func (a *Args) Set(key, value string) {
a.bufKV.value = AppendBytesStr(a.bufKV.value[:0], value)
a.SetBytes(key, a.bufKV.value)
}
func (a *Args) SetBytes(key string, value []byte) {
a.bufKV.key = AppendBytesStr(a.bufKV.key[:0], key)
a.args = setKV(a.args, a.bufKV.key, value)
}
func setKV(h []argsKV, key, value []byte) []argsKV {
n := len(h)
for i := 0; i < n; i++ {
kv := &h[i]
if bytes.Equal(kv.key, key) {
kv.value = append(kv.value[:0], value...)
return h
}
}
if cap(h) > n {
h = h[:n+1]
kv := &h[n]
kv.key = append(kv.key[:0], key...)
kv.value = append(kv.value[:0], value...)
return h
}
var kv argsKV
kv.key = append(kv.key, key...)
kv.value = append(kv.value, value...)
return append(h, kv)
}
func peekKV(h []argsKV, k []byte) []byte {
for i, n := 0, len(h); i < n; i++ {
kv := &h[i]
if bytes.Equal(k, kv.key) {
return kv.value
}
}
return nil
}
func (a *Args) GetBytes(dst []byte, key string) []byte {
value := a.Peek(key)
return append(dst[:0], value...)
}
// Warning: each call allocates memory for returned string.
func (a *Args) Get(key string) string {
return string(a.Peek(key))
}
func (a *Args) Peek(key string) []byte {
a.bufKV.key = AppendBytesStr(a.bufKV.key[:0], key)
return peekKV(a.args, a.bufKV.key)
}
func (a *Args) Has(key string) bool {
return a.Peek(key) != nil
}
var errNoArgValue = errors.New("No value for the given key")
func (a *Args) GetUint(key string) (int, error) {
value := a.Peek(key)
if len(value) == 0 {
return -1, errNoArgValue
}
return parseUint(value)
}
func (a *Args) GetUintOrZero(key string) int {
n, err := a.GetUint(key)
if err != nil {
n = 0
}
return n
}
func (a *Args) GetUfloat(key string) (float64, error) {
value := a.Peek(key)
if len(value) == 0 {
return -1, errNoArgValue
}
return parseUfloat(value)
}
func (a *Args) GetUfloatOrZero(key string) float64 {
f, err := a.GetUfloat(key)
if err != nil {
f = 0
}
return f
}
func EqualBytesStr(b []byte, s string) bool {
if len(s) != len(b) {
return false
}
for i, n := 0, len(s); i < n; i++ {
if s[i] != b[i] {
return false
}
}
return true
}
func AppendBytesStr(dst []byte, src string) []byte {
for i, n := 0, len(src); i < n; i++ {
dst = append(dst, src[i])
}
return dst
}
type argsParser struct {
b []byte
}
func (p *argsParser) Init(buf []byte) {
p.b = buf
}
func (p *argsParser) Next(kv *argsKV) bool {
for {
if !p.next(kv) {
return false
}
if len(kv.key) > 0 || len(kv.value) > 0 {
if kv.key == nil {
kv.key = []byte{}
}
if kv.value == nil {
kv.value = []byte{}
}
return true
}
}
}
func (p *argsParser) next(kv *argsKV) bool {
if len(p.b) == 0 {
return false
}
n := bytes.IndexByte(p.b, '&')
var b []byte
if n < 0 {
b = p.b
p.b = p.b[len(p.b):]
} else {
b = p.b[:n]
p.b = p.b[n+1:]
}
n = bytes.IndexByte(b, '=')
if n < 0 {
kv.key = decodeArg(kv.key[:0], b, true)
kv.value = kv.value[:0]
} else {
kv.key = decodeArg(kv.key[:0], b[:n], true)
kv.value = decodeArg(kv.value[:0], b[n+1:], true)
}
return true
}
func decodeArg(dst, src []byte, decodePlus bool) []byte {
for i, n := 0, len(src); i < n; i++ {
c := src[i]
switch c {
case '+':
if decodePlus {
c = ' '
}
dst = append(dst, c)
case '%':
if i+2 >= n {
return append(dst, src[i:]...)
}
x1 := unhex(src[i+1])
x2 := unhex(src[i+2])
if x1 < 0 || x2 < 0 {
dst = append(dst, c)
} else {
dst = append(dst, byte(x1<<4|x2))
i += 2
}
default:
dst = append(dst, c)
}
}
return dst
}
func unhex(c byte) int {
if c >= '0' && c <= '9' {
return int(c - '0')
}
if c >= 'a' && c <= 'f' {
return 10 + int(c-'a')
}
if c >= 'A' && c <= 'F' {
return 10 + int(c-'A')
}
return -1
}
func appendQuotedArg(dst, v []byte) []byte {
for _, c := range v {
if c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '/' {
dst = append(dst, c)
} else {
dst = append(dst, '%', hexChar(c>>4), hexChar(c&15))
}
}
return dst
}
func hexChar(c byte) byte {
if c < 10 {
return '0' + c
}
return c - 10 + 'A'
}