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

Code before:
	c := app.AcquireCtx(&fasthttp.RequestCtx{})

	require.True(t, c.Stale())
}

// go test -run Test_Ctx_Subdomains
func Test_Ctx_Subdomains(t *testing.T) {
	t.Parallel()
	app := New()
	c := app.AcquireCtx(&fasthttp.RequestCtx{})

	c.Request().URI().SetHost("john.doe.is.awesome.google.com")
	require.Equal(t, []string{"john", "doe"}, c.Subdomains(4))

	c.Request().URI().SetHost("localhost:3000")
	require.Empty(t, c.Subdomains())
}

// go test -run Test_Ctx_Subdomains_OffsetTooHigh
func Test_Ctx_Subdomains_OffsetTooHigh(t *testing.T) {
	t.Parallel()
	app := New()
	c := app.AcquireCtx(&fasthttp.RequestCtx{})

	c.Request().URI().SetHost("john.doe.is.awesome.google.com")
	require.Empty(t, c.Subdomains(10))
}

// go test -v -run=^$ -bench=Benchmark_Ctx_Subdomains -benchmem -count=4
func Benchmark_Ctx_Subdomains(b *testing.B) {
	app := New()
	c := app.AcquireCtx(&fasthttp.RequestCtx{})

	c.Request().SetRequestURI("http://john.doe.google.com")
	var res []string
	b.ReportAllocs()
	for b.Loop() {
		res = c.Subdomains()
	}
	require.Equal(b, []string{"john", "doe"}, res)
}

// go test -run Test_Ctx_ClearCookie
func Test_Ctx_ClearCookie(t *testing.T) {
	t.Parallel()
	app := New()
	c := app.AcquireCtx(&fasthttp.RequestCtx{})

	c.Request().Header.Set(HeaderCookie, "john=doe")
	c.Res().ClearCookie("john")
	require.True(t, strings.HasPrefix(string(c.Response().Header.Peek(HeaderSetCookie)), "john=; expires="))

Review comment (suggestion):
The PR summary mentions handling negative offsets, but the new test only covers a large positive offset (c.Subdomains(10)). While the logic handles negative offsets correctly (since l < 0 triggers the same []string{} return), there’s no explicit test for this case.

Code after:
	c := app.AcquireCtx(&fasthttp.RequestCtx{})

	require.True(t, c.Stale())
}

// go test -run Test_Ctx_Subdomains
func Test_Ctx_Subdomains(t *testing.T) {
	app := New()

	type tc struct {
		name   string
		host   string
		offset []int // nil ⇒ call without argument
		want   []string
	}

	cases := []tc{
		{
			name:   "default offset (2) drops registrable domain + TLD",
			host:   "john.doe.is.awesome.google.com",
			offset: nil, // Subdomains()
			want:   []string{"john", "doe", "is", "awesome"},
		},
		{
			name:   "custom offset trims N right-hand labels",
			host:   "john.doe.is.awesome.google.com",
			offset: []int{4},
			want:   []string{"john", "doe"},
		},
		{
			name:   "offset too high returns empty",
			host:   "john.doe.is.awesome.google.com",
			offset: []int{10},
			want:   []string{},
		},
		{
			name:   "zero offset returns all labels",
			host:   "john.doe.google.com",
			offset: []int{0},
			want:   []string{"john", "doe", "google", "com"},
		},
		{
			name:   "offset 1 keeps registrable domain",
			host:   "john.doe.google.com",
			offset: []int{1},
			want:   []string{"john", "doe", "google"},
		},
		{
			name:   "negative offset returns empty",
			host:   "john.doe.google.com",
			offset: []int{-1},
Repository: gofiber/fiber
File: client/request.go (Go)

Code before:

// FormData returns all values associated with a form field.
func (r *Request) FormData(key string) []string {
	var res []string
	tmp := r.formData.PeekMulti(key)
	for _, v := range tmp {
		res = append(res, utils.UnsafeString(v))
	}
	return res
}

// AllFormData returns an iterator over all form fields.
// Use maps.Collect() to gather them into a map if needed.
//
// The returned values are only valid until the request object is released.
// Do not store references to returned values; make copies instead.
func (r *Request) AllFormData() iter.Seq2[string, []string] {
	return func(yield func(string, []string) bool) {
		vals := r.formData.Len()
		prealloc := make([]string, 2*vals)
		p := pair{
			k: prealloc[:0:vals],
			v: prealloc[vals : vals : 2*vals],
		}
		for k, v := range r.formData.All() {
			p.k = append(p.k, utils.UnsafeString(k))
			p.v = append(p.v, utils.UnsafeString(v))
		}
		sort.Sort(&p)

		j := 0
		for i := range vals {
			if i == vals-1 || p.k[i] != p.k[i+1] {
				if !yield(p.k[i], p.v[j:i+1]) {
					break
				}
				j = i + 1
			}
		}
	}
}

// AddFormData adds a single form field and value to the Request.
func (r *Request) AddFormData(key, val string) *Request {
	r.formData.Add(key, val)
	r.resetBody(formBody)
	return r
}

// SetFormData sets a single form field and value, overriding any previously set value.
func (r *Request) SetFormData(key, val string) *Request {

Review comment (suggestion):
Before calling make, do this:

```go
if vals == 0 {
    return
}
```

Code after:
	}
	r.bodyType = t
}

// FormData returns all values associated with a form field.
func (r *Request) FormData(key string) []string {
	var res []string
	tmp := r.formData.PeekMulti(key)
	for _, v := range tmp {
		res = append(res, utils.UnsafeString(v))
	}
	return res
}

// AllFormData returns an iterator over all form fields.
// Use maps.Collect() to gather them into a map if needed.
//
// The returned values are only valid until the request object is released.
// Do not store references to returned values; make copies instead.
func (r *Request) AllFormData() iter.Seq2[string, []string] {
	return func(yield func(string, []string) bool) {
		vals := r.formData.Len()
		if vals == 0 {
			return
		}
		prealloc := make([]string, 2*vals)
		p := pair{
			k: prealloc[:0:vals],
			v: prealloc[vals : vals : 2*vals],
		}
		for k, v := range r.formData.All() {
			p.k = append(p.k, utils.UnsafeString(k))
			p.v = append(p.v, utils.UnsafeString(v))
		}
		sort.Sort(&p)

		j := 0
		for i := range vals {
			if i == vals-1 || p.k[i] != p.k[i+1] {
				if !yield(p.k[i], p.v[j:i+1]) {
					break
				}
				j = i + 1
			}
		}
	}
}

// AddFormData adds a single form field and value to the Request.
func (r *Request) AddFormData(key, val string) *Request {
	r.formData.Add(key, val)
Repository: gofiber/fiber
File: client/request.go (Go)

Code before:
	r.header.SetHeaders(h)
	return r
}

// Param returns all values associated with the given query parameter.
func (r *Request) Param(key string) []string {
	var res []string
	tmp := r.params.PeekMulti(key)
	for _, v := range tmp {
		res = append(res, utils.UnsafeString(v))
	}
	return res
}

// Params returns an iterator over all query parameters in the Request.
// Use maps.Collect() to gather them into a map if needed.
//
// The returned values are only valid until the request object is released.
// Do not store references to returned values; make copies instead.
func (r *Request) Params() iter.Seq2[string, []string] {
	return func(yield func(string, []string) bool) {
		vals := r.params.Len()
		prealloc := make([]string, 2*vals)
		p := pair{
			k: prealloc[:0:vals],
			v: prealloc[vals : vals : 2*vals],
		}
		for k, v := range r.params.All() {
			p.k = append(p.k, utils.UnsafeString(k))
			p.v = append(p.v, utils.UnsafeString(v))
		}
		sort.Sort(&p)

		j := 0
		for i := range vals {
			if i == vals-1 || p.k[i] != p.k[i+1] {
				if !yield(p.k[i], p.v[j:i+1]) {
					break
				}
				j = i + 1
			}
		}
	}
}

// AddParam adds a single query parameter and value to the Request.
func (r *Request) AddParam(key, val string) *Request {
	r.params.Add(key, val)
	return r
}


Review comment (suggestion):
Before calling make, do this:

```go
if vals == 0 {
    return
}
```

Code after:
	r.header.SetHeaders(h)
	return r
}

// Param returns all values associated with the given query parameter.
func (r *Request) Param(key string) []string {
	var res []string
	tmp := r.params.PeekMulti(key)
	for _, v := range tmp {
		res = append(res, utils.UnsafeString(v))
	}
	return res
}

// Params returns an iterator over all query parameters in the Request.
// Use maps.Collect() to gather them into a map if needed.
//
// The returned values are only valid until the request object is released.
// Do not store references to returned values; make copies instead.
func (r *Request) Params() iter.Seq2[string, []string] {
	return func(yield func(string, []string) bool) {
		vals := r.params.Len()
		if vals == 0 {
			return
		}
		prealloc := make([]string, 2*vals)
		p := pair{
			k: prealloc[:0:vals],
			v: prealloc[vals : vals : 2*vals],
		}
		for k, v := range r.params.All() {
			p.k = append(p.k, utils.UnsafeString(k))
			p.v = append(p.v, utils.UnsafeString(v))
		}
		sort.Sort(&p)

		j := 0
		for i := range vals {
			if i == vals-1 || p.k[i] != p.k[i+1] {
				if !yield(p.k[i], p.v[j:i+1]) {
					break
				}
				j = i + 1
			}
		}
	}
}

// AddParam adds a single query parameter and value to the Request.
func (r *Request) AddParam(key, val string) *Request {
	r.params.Add(key, val)
Repository: gofiber/fiber
File: client/request.go (Go)

Code before:
// Len implements sort.Interface and reports the number of tracked keys.
func (p *pair) Len() int {
	return len(p.k)
}

// Swap implements sort.Interface and swaps the entries at the provided indices.
func (p *pair) Swap(i, j int) {
	p.k[i], p.k[j] = p.k[j], p.k[i]
	p.v[i], p.v[j] = p.v[j], p.v[i]
}

// Less implements sort.Interface and orders entries lexicographically by key.
func (p *pair) Less(i, j int) bool {
	return p.k[i] < p.k[j]
}

// Headers returns an iterator over all headers in the Request.
// Use maps.Collect() to gather them into a map if needed.
//
// The returned values are only valid until the request object is released.
// Do not store references to returned values; make copies instead.
func (r *Request) Headers() iter.Seq2[string, []string] {
	return func(yield func(string, []string) bool) {
		peekKeys := r.header.PeekKeys()

		// It is necessary to have immutable byte slice.
		keys := make([]string, len(peekKeys))
		for i, key := range peekKeys {
			keys[i] = utils.UnsafeString(key)
		}

		for _, key := range keys {
			vals := r.header.PeekAll(key)
			valsStr := make([]string, len(vals))
			for i, v := range vals {
				valsStr[i] = utils.UnsafeString(v)
			}

			if !yield(key, valsStr) {
				return
			}
		}
	}
}

// AddHeader adds a single header field and value to the Request.
func (r *Request) AddHeader(key, val string) *Request {
	r.header.Add(key, val)
	return r
}


Review comment (suggestion):
@arturmelanchyk the comment is a little bit confusing, since you create a byte slice with mutable values out of the key


```suggestion
```

Code after:
// Len implements sort.Interface and reports the number of tracked keys.
func (p *pair) Len() int {
	return len(p.k)
}

// Swap implements sort.Interface and swaps the entries at the provided indices.
func (p *pair) Swap(i, j int) {
	p.k[i], p.k[j] = p.k[j], p.k[i]
	p.v[i], p.v[j] = p.v[j], p.v[i]
}

// Less implements sort.Interface and orders entries lexicographically by key.
func (p *pair) Less(i, j int) bool {
	return p.k[i] < p.k[j]
}

// Headers returns an iterator over all headers in the Request.
// Use maps.Collect() to gather them into a map if needed.
//
// The returned values are only valid until the request object is released.
// Do not store references to returned values; make copies instead.
func (r *Request) Headers() iter.Seq2[string, []string] {
	return func(yield func(string, []string) bool) {
		peekKeys := r.header.PeekKeys()

		// Copy keys to immutable strings to decouple from fasthttp's internal buffers.
		keys := make([]string, len(peekKeys))
		for i, key := range peekKeys {
			keys[i] = utils.UnsafeString(key)
		}

		for _, key := range keys {
			vals := r.header.PeekAll(key)
			valsStr := make([]string, len(vals))
			for i, v := range vals {
				valsStr[i] = utils.UnsafeString(v)
			}

			if !yield(key, valsStr) {
				return
			}
		}
	}
}

// AddHeader adds a single header field and value to the Request.
func (r *Request) AddHeader(key, val string) *Request {
	r.header.Add(key, val)
	return r
}
Repository: gofiber/fiber
File: ctx.go (Go)

Code before:
		}
	}

	return len(c.fasthttp.Request.Body()) > 0
}

// UpdateParam overwrites a route parameter value by name.
// If the parameter name does not exist in the route, this method does nothing.
func (c *DefaultCtx) UpdateParam(name, value string) {
	// If no route is matched, there are no parameters to update
	if !c.Matched() {
		return
	}

	// Normalize wildcard (*) and plus (+) tokens to their internal
	// representations (*1, +1) used by the router.
	if name == "*" || name == "+" {
		name += "1"
	}

	if c.app.config.CaseSensitive {
		for i, param := range c.route.Params {
			// Prevent out-of-bounds access if route params exceed allocated values
			if i >= maxParams {
				return
			}
			if param == name {
				c.values[i] = value
				return
			}
		}
		return
	}

	nameBytes := utils.UnsafeBytes(name)
	for i, param := range c.route.Params {
		// Prevent out-of-bounds access if route params exceed allocated values
		if i >= maxParams {
			return
		}
		if utils.EqualFold(utils.UnsafeBytes(param), nameBytes) {
			c.values[i] = value
			return
		}
	}
}

func hasTransferEncodingBody(hdr *fasthttp.RequestHeader) bool {
	teBytes := hdr.Peek(HeaderTransferEncoding)
	var te string


Review comment (suggestion):
### **The bounds check `i >= maxParams` IS NECESSARY - but for a different reason than I initially thought**

1. **No validation during route registration**: The `parseRoute` function in path.go does NOT validate or limit the number of parameters. It will happily parse routes with 50+ parameters.

2. **The framework CAN have routes with > maxParams**: There's nothing preventing registration of such routes.

Code after:
		}
	}

	return len(c.fasthttp.Request.Body()) > 0
}

// OverrideParam overwrites a route parameter value by name.
// If the parameter name does not exist in the route, this method does nothing.
func (c *DefaultCtx) OverrideParam(name, value string) {
	// If no route is matched, there are no parameters to update
	if !c.Matched() {
		return
	}

	// Normalize wildcard (*) and plus (+) tokens to their internal
	// representations (*1, +1) used by the router.
	if name == "*" || name == "+" {
		name += "1"
	}

	if c.app.config.CaseSensitive {
		for i, param := range c.route.Params {
			if param == name {
				c.values[i] = value
				return
			}
		}
		return
	}

	nameBytes := utils.UnsafeBytes(name)
	for i, param := range c.route.Params {
		if utils.EqualFold(utils.UnsafeBytes(param), nameBytes) {
			c.values[i] = value
			return
		}
	}
}

func hasTransferEncodingBody(hdr *fasthttp.RequestHeader) bool {
	teBytes := hdr.Peek(HeaderTransferEncoding)
	var te string

	if len(teBytes) > 0 {
		te = utils.UnsafeString(teBytes)
	} else {
		for key, value := range hdr.All() {
			if !utils.EqualFold(utils.UnsafeString(key), HeaderTransferEncoding) {
				continue
			}
			te = utils.UnsafeString(value)
Repository: gojue/ecapture
File: user/imodule.go (Go)

Code before:
	for {
		select {
		case _ = <-this.ctx.Done():
			err := this.child.Stop()
			if err != nil {
				this.logger.Fatalf("%s\t stop Module error:%v.", this.child.Name(), err)
			}
			return
		}
	}
}

func (this *Module) readEvents() error {
	var errChan = make(chan error, 8)
	for _, event := range this.child.Events() {
		switch {
		case event.Type() == ebpf.RingBuf:
			go this.ringbufEventReader(errChan, event)
		case event.Type() == ebpf.PerfEventArray:
			go this.perfEventReader(errChan, event)
		default:
			errChan <- fmt.Errorf("%s\tNot support mapType:%s , mapinfo:%s", this.child.Name(), event.Type().String(), event.String())
		}
	}

	for {
		select {
		case err := <-errChan:
			return err
		}
	}
}

func (this *Module) perfEventReader(errChan chan error, em *ebpf.Map) {
	rd, err := perf.NewReader(em, os.Getpagesize()*64)
	if err != nil {
		errChan <- fmt.Errorf("creating %s reader dns: %s", em.String(), err)
		return
	}
	defer rd.Close()
	for {
		//判断ctx是不是结束
		select {
		case _ = <-this.ctx.Done():
			this.logger.Printf("%s\tperfEventReader received close signal from context.Done().", this.child.Name())
			return
		default:
		}

		record, err := rd.Read()
		if err != nil {

Review comment (refactor):
Maybe simplify this as
```
go func() {
    for err := <- errChan {
    }
}

Code after:
	for {
		select {
		case _ = <-this.ctx.Done():
			err := this.child.Stop()
			if err != nil {
				this.logger.Fatalf("%s\t stop Module error:%v.", this.child.Name(), err)
			}
			return
		}
	}
}

func (this *Module) readEvents() error {
	var errChan = make(chan error, 8)
	for _, event := range this.child.Events() {
		switch {
		case event.Type() == ebpf.RingBuf:
			this.ringbufEventReader(errChan, event)
		case event.Type() == ebpf.PerfEventArray:
			this.perfEventReader(errChan, event)
		default:
			return fmt.Errorf("%s\tNot support mapType:%s , mapinfo:%s", this.child.Name(), event.Type().String(), event.String())
		}
	}

	go func() {
		for {
			select {
			case err := <-errChan:
				this.logger.Printf("%s\treadEvents error:%v", this.child.Name(), err)
			}
		}
	}()
	return nil
}

func (this *Module) perfEventReader(errChan chan error, em *ebpf.Map) {
	rd, err := perf.NewReader(em, os.Getpagesize()*64)
	if err != nil {
		errChan <- fmt.Errorf("creating %s reader dns: %s", em.String(), err)
		return
	}
	this.reader = append(this.reader, rd)
	go func() {
		for {
			//判断ctx是不是结束
			select {
			case _ = <-this.ctx.Done():
				this.logger.Printf("%s\tperfEventReader received close signal from context.Done().", this.child.Name())
				return
			default:
Repository: gojue/ecapture
File: user/event/event_bash.go (Go)

Code before:
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package event

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

/*
  u8 type;
  u32 pid;
  u32 uid;
  u8 line[MAX_DATA_SIZE_BASH];
  u32 retval;
  char comm[TASK_COMM_LEN];
*/

const MaxDataSizeBash = 256

type BashEvent struct {
	eventType EventType
	Type      uint32                 `json:"type"`
	Pid       uint32                 `json:"pid"`
	Uid       uint32                 `json:"uid"`
	Line      [MaxDataSizeBash]uint8 `json:"line"`
	Retval    uint32                 `json:"Retval"`
	Comm      [16]byte               `json:"Comm"`
	AllLines  string
}

func (be *BashEvent) Decode(payload []byte) (err error) {
	buf := bytes.NewBuffer(payload)
	if err = binary.Read(buf, binary.LittleEndian, &be.Type); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Pid); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Uid); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Line); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Retval); err != nil {
		return
	}

Review comment (suggestion):
Not a good naming, the lowercase `type` is a key syntax in Go, it is recommended to change it to `bashType`.

Code after:
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package event

import (
	"bytes"
	"encoding/binary"
	"fmt"
)

/*
  u8 type;
  u32 pid;
  u32 uid;
  u8 line[MAX_DATA_SIZE_BASH];
  u32 retval;
  char comm[TASK_COMM_LEN];
*/

const MaxDataSizeBash = 256

type BashEvent struct {
	eventType EventType
	BashType  uint32                 `json:"bashtype"`
	Pid       uint32                 `json:"pid"`
	Uid       uint32                 `json:"uid"`
	Line      [MaxDataSizeBash]uint8 `json:"line"`
	Retval    uint32                 `json:"Retval"`
	Comm      [16]byte               `json:"Comm"`
	AllLines  string
}

func (be *BashEvent) Decode(payload []byte) (err error) {
	buf := bytes.NewBuffer(payload)
	if err = binary.Read(buf, binary.LittleEndian, &be.BashType); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Pid); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Uid); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Line); err != nil {
		return
	}
	if err = binary.Read(buf, binary.LittleEndian, &be.Retval); err != nil {
		return
	}
Repository: gojue/ecapture
File: cli/cmd/env_detection.go (Go)

Code before:
			return fmt.Errorf("the Linux/Android Kernel version %v (x86_64) is not supported. Requires a version greater than 4.18.", kv)
		}
	case "arm64":
		if kv < kernel.VersionCode(5, 5, 0) {
			return fmt.Errorf("the Linux/Android Kernel version %v (aarch64) is not supported. Requires a version greater than 5.5.", kv)
		}
	default:
		return fmt.Errorf("unsupported CPU arch:%v", runtime.GOARCH)
	}

	return nil
}
func detectBpfCap() error {
	// BPF 权限检测
	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
		Name: "uprobe_dummy",
		Type: ebpf.Kprobe,
		Instructions: asm.Instructions{
			asm.Mov.Imm(asm.R0, 0),
			asm.Return(),
		},
		License: "GPL",
	})
	if err != nil {
		if errors.Is(err, unix.EPERM) {
			return fmt.Errorf("the current user does not have CAP_BPF to load bpf programs. Please run as root or use sudo.")
		}

		return fmt.Errorf("failed to create bpf program: %v", err)
	}
	defer prog.Close()

	return nil
}

func detectEnv() error {
	// 环境检测

	if err := detectKernel(); err != nil {
		return err
	}

	if err := detectBpfCap(); err != nil {
		return err
	}

	return nil
}


Review comment (security):
Can some reminders be added for the container approach?

> add the `--privileged=true` flag for Docker ?

Code after:
			return fmt.Errorf("the Linux/Android Kernel version %v (x86_64) is not supported. Requires a version greater than 4.18.", kv)
		}
	case "arm64":
		if kv < kernel.VersionCode(5, 5, 0) {
			return fmt.Errorf("the Linux/Android Kernel version %v (aarch64) is not supported. Requires a version greater than 5.5.", kv)
		}
	default:
		return fmt.Errorf("unsupported CPU arch:%v", runtime.GOARCH)
	}

	return nil
}
func detectBpfCap() error {
	// BPF 权限检测
	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
		Name: "uprobe_dummy",
		Type: ebpf.Kprobe,
		Instructions: asm.Instructions{
			asm.Mov.Imm(asm.R0, 0),
			asm.Return(),
		},
		License: "GPL",
	})
	if err != nil {
		if errors.Is(err, unix.EPERM) {
			return fmt.Errorf("the current user does not have CAP_BPF to load bpf programs. Please run as root or use sudo or add the --privileged=true flag for Docker.")
		}

		return fmt.Errorf("failed to create bpf program: %v", err)
	}
	defer prog.Close()

	return nil
}

func detectEnv() error {
	// 环境检测

	if err := detectKernel(); err != nil {
		return err
	}

	if err := detectBpfCap(); err != nil {
		return err
	}

	return nil
}