Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add httpx: low memory, non-allocating http implementation #13

Merged
merged 6 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions httpx/args.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package httpx

import "unsafe"

const (
argsNoValue = true
argsHasValue = false
)

type argsKV struct {
key []byte
value []byte
noValue bool
}

func visitArgs(args []argsKV, f func(k, v []byte)) {
for i, n := 0, len(args); i < n; i++ {
kv := &args[i]
f(kv.key, kv.value)
}

Check warning on line 20 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L16-L20

Added lines #L16 - L20 were not covered by tests
}

func visitArgsKey(args []argsKV, f func(k []byte)) {
for i, n := 0, len(args); i < n; i++ {
kv := &args[i]
f(kv.key)
}

Check warning on line 27 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L23-L27

Added lines #L23 - L27 were not covered by tests
}

func copyArgs(dst, src []argsKV) []argsKV {
if cap(dst) < len(src) {
tmp := make([]argsKV, len(src))
dstLen := len(dst)
dst = dst[:cap(dst)] // copy all of dst.
copy(tmp, dst)
for i := dstLen; i < len(tmp); i++ {
// Make sure nothing is nil.
tmp[i].key = []byte{}
tmp[i].value = []byte{}
}
dst = tmp

Check warning on line 41 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L30-L41

Added lines #L30 - L41 were not covered by tests
}
n := len(src)
dst = dst[:n]
for i := 0; i < n; i++ {
dstKV := &dst[i]
srcKV := &src[i]
dstKV.key = append(dstKV.key[:0], srcKV.key...)
if srcKV.noValue {
dstKV.value = dstKV.value[:0]
} else {
dstKV.value = append(dstKV.value[:0], srcKV.value...)
}
dstKV.noValue = srcKV.noValue

Check warning on line 54 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L43-L54

Added lines #L43 - L54 were not covered by tests
}
return dst

Check warning on line 56 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L56

Added line #L56 was not covered by tests
}

func delAllArgs(args []argsKV, key string) []argsKV {
for i, n := 0, len(args); i < n; i++ {
kv := &args[i]
if key == b2s(kv.key) {
tmp := *kv
copy(args[i:], args[i+1:])
n--
i--
args[n] = tmp
args = args[:n]
}

Check warning on line 69 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L59-L69

Added lines #L59 - L69 were not covered by tests
}
return args

Check warning on line 71 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L71

Added line #L71 was not covered by tests
}

func setArg(h []argsKV, key, value string, noValue bool) []argsKV {
n := len(h)
for i := 0; i < n; i++ {
kv := &h[i]
if key == b2s(kv.key) {
if noValue {
kv.value = kv.value[:0]
} else {
kv.value = append(kv.value[:0], value...)
}
kv.noValue = noValue
return h

Check warning on line 85 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L74-L85

Added lines #L74 - L85 were not covered by tests
}
}
return appendArg(h, key, value, noValue)

Check warning on line 88 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L88

Added line #L88 was not covered by tests
}

func appendArg(args []argsKV, key, value string, noValue bool) []argsKV {
var kv *argsKV
args, kv = allocArg(args)
kv.key = append(kv.key[:0], key...)
if noValue {
kv.value = kv.value[:0]

Check warning on line 96 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L96

Added line #L96 was not covered by tests
} else {
kv.value = append(kv.value[:0], value...)
}
kv.noValue = noValue
return args
}

func allocArg(h []argsKV) ([]argsKV, *argsKV) {
n := len(h)
if cap(h) > n {
h = h[:n+1]

Check warning on line 107 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L107

Added line #L107 was not covered by tests
} else {
h = append(h, argsKV{
value: []byte{},
})
}
return h, &h[n]
}

func releaseArg(h []argsKV) []argsKV {
return h[:len(h)-1]

Check warning on line 117 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L116-L117

Added lines #L116 - L117 were not covered by tests
}

func hasArg(h []argsKV, key string) bool {
for i, n := 0, len(h); i < n; i++ {
kv := &h[i]
if key == b2s(kv.key) {
return true
}

Check warning on line 125 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L120-L125

Added lines #L120 - L125 were not covered by tests
}
return false

Check warning on line 127 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L127

Added line #L127 was not covered by tests
}

func peekArgStr(h []argsKV, k string) []byte {
for i, n := 0, len(h); i < n; i++ {
kv := &h[i]
if b2s(kv.key) == k {
return kv.value
}

Check warning on line 135 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L134-L135

Added lines #L134 - L135 were not covered by tests
}
return nil
}

// b2s converts byte slice to a string without memory allocation.
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
func b2s(b []byte) string {
return unsafe.String(unsafe.SliceData(b), len(b))
}

// s2b converts string to a byte slice without memory allocation.
func s2b(s string) []byte {
return unsafe.Slice(unsafe.StringData(s), len(s))

Check warning on line 148 in httpx/args.go

View check run for this annotation

Codecov / codecov/patch

httpx/args.go#L147-L148

Added lines #L147 - L148 were not covered by tests
}
Loading
Loading