goreset

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

README

goreset

Go Reference Go Coverage

goreset generates Reset() methods for structs that are reused through a sync.Pool or a free list: the value goes back to its zero state, but keeps the memory it owns.

Why goreset?

A pooled object has to be cleaned before reuse. *r = T{} is the fast way to do it, and the wrong one: it drops every slice's backing array, forgets a reusable *time.Timer, and does not compile for a struct holding a mutex. A hand-written Reset keeps all of that — until a new field is added and forgotten there. goreset writes the method from the struct definition, so it is always complete, and chooses the cheapest form for the type:

// Code generated by goreset; DO NOT EDIT.

// Reset zeroes r in one assignment and gives the slices their arrays back.
func (r *Request) Reset() {
	clear(r.Data)
	keepData := r.Data[:0]
	keepTimer := r.Timer

	*r = Request{}
	r.Data = keepData
	r.Mask = ^uint32(0)
	r.Timer = keepTimer
	if r.Timer != nil {
		r.Timer.Stop()
	}
}

What it does per field

Field Reset
slice clear(s); s = s[:0] — the backing array is kept
pointer, map, chan, interface, func nil
numbers, bool, string, arrays, plain structs zero value
value with its own Reset() r.F.Reset()
pointer with its own Reset() kept; r.F.Reset() when not nil
atomic.* Store(0) / Store(false) / Store(nil)
sync.* (Mutex, WaitGroup, Pool...) left alone

Struct tags change the treatment of a field:

Tag Effect
reset:"-" the field is left as it is
reset:"value=EXPR" set to EXPR instead of zero, e.g. value=^uint32(0)
reset:"value=pkg.Const,import=example.com/pkg" the same with an import the expression needs
reset:"call=Method" kept; r.F.Method() is called (call=Stop for a *time.Timer)
reset:"release=Func" pointer with Reset(): Func(r.F) is called and the field set to nil

Two modes

Bulk: the fields to keep are saved, the struct is zeroed in one assignment, the kept fields are put back. One memclr for the whole struct, no per-field code for the zeroed ones.

Field by field: one statement per field; sync fields are not touched.

The mode is chosen per type by a cost model, in units of one field store:

bulk field by field
the struct memclr: 1 per 16 bytes —
plain field (number, pointer, string, atomic...) 0 1
slice 3 (clear, keep, restore) 2 (clear, reslice)
value with Reset() 2 + 2 per 16 bytes (copied out and back) 1 (the call)
pointer with Reset(), call=, reset:"-" 2 (keep, restore) 1
value= 1 1
array, struct without Reset() 0 1 per 16 bytes

The cheaper mode is generated; a sync field forces field-wise mode whatever the costs. The constants were fitted on two real structs (700 bytes / 95 fields: both modes equal; 2400 bytes / 74 fields: field-wise 2.5x faster) — a model, not a measurement, so a struct near the break-even point is worth a benchmark of its own.

The comment of the generated method says which mode and why:

// Reset zeroes r field by field, not in one assignment:
//   - zeroing all 2400 bytes and restoring 39 fields costs 314, resetting the 74 fields one by one costs 162

and the generator says so on stderr too, with what would change the outcome:

goreset: wrote reqvars_reset.gen.go
goreset: ReqVars is reset field by field; for one assignment:
  - ReqVars: one assignment would zero all 2400 bytes and restore 39 fields (cost 314) where resetting the 74 fields one by one costs 162; fewer slices and kept values, or a smaller struct, would turn that around

For a lock the hint is to move it behind a pointer or out of the pooled value. Package.Analyze returns the mode, both costs and the hints to other tools.

Installation

go get -tool github.com/darxnet/goreset/cmd/goreset

Requires Go 1.24 (go get -tool). No dependencies: the package is loaded with go list -export and go/importer from the standard library.

Usage

Put the directive next to the type and run go generate:

//go:generate go tool goreset -type=Request,Response
type Request struct {
	Data  []byte
	Timer *time.Timer `reset:"call=Stop"`
	Mask  uint32      `reset:"value=^uint32(0)"`
	Mu    sync.Mutex
}

This writes request_reset.gen.go and response_reset.gen.go in the same package. -dir points the generator at another package.

The generator is also a library — goreset.Load and Package.Generate — for tools that want the source without the file.

Rules of the generated Reset

  • It must run without concurrent users of the value: atomics are stored, not swapped, and bulk mode copies the struct.
  • Slices keep their capacity. A field that must not keep memory between uses (a buffer that may grow very large once) should be tagged reset:"-" and handled by hand, or released through release=.
  • A nested value's Reset() is called after the bulk assignment, so it sees its own state, not zeroes.

See also

The sibling modules follow the same rule — nothing allocated on the hot path, the zero value ready to use:

  • goshard — sharded, lock-based concurrent map for high-churn data: stores allocate nothing
  • gosync — generic concurrent map with sync.Map semantics and no interface boxing, for read-mostly data
  • gocopy — generates CopyFrom/Copy that reuse the destination's memory instead of allocating

License

Apache License 2.0. See LICENSE for details.

Documentation

Overview

Package goreset generates Reset methods for structs that are reused through a pool.

A generated Reset puts the value back to its zero state without giving up the memory the value owns: slices keep their backing arrays (cleared, length zero), nested values with their own Reset are reset through it, and struct tags choose a different treatment per field:

Buf   []byte       `reset:"-"`                 // left as it is
Timer *time.Timer  `reset:"call=Stop"`         // kept, method called
Flags uint32       `reset:"value=^uint32(0)"`  // set to EXPR, not zero
Conn  *Conn        `reset:"release=PutConn"`   // PutConn(r.Conn); r.Conn = nil

Fields of sync types are never touched, and a struct holding one is reset field by field. Any other struct is reset either in one assignment (`*r = T{}`) with the kept fields restored after it, or field by field, whichever a cost model built on the field sizes rates cheaper; the generated comment says which and why.

The usual way to run it is a go:generate directive next to the type:

//go:generate go tool goreset -type=Request

which writes request_reset.gen.go in the same package. Load and Package.Generate expose the generator to other tools.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoPackage = errors.New("goreset: no package found")

ErrNoPackage is returned by Load when dir holds no Go package.

Functions

func FileName

func FileName(typeName string) string

FileName is the file Generate's output is meant for: <type>_reset.gen.go, lower case.

Types

type Package

type Package struct {
	// contains filtered or unexported fields
}

Package is a loaded Go package whose struct types Generate can describe.

func Load

func Load(dir string) (*Package, error)

Load type-checks the package in dir (the current directory when empty).

The dependencies come as export data from `go list -export`; the package itself is checked from source and its own type errors are tolerated, so a stale generated file (one that names a field that no longer exists) does not stop the type from being generated again. A dependency that does not compile is an error.

func (*Package) Analyze added in v1.2.0

func (p *Package) Analyze(typeName string) (Result, error)

Analyze is Generate with the findings behind the output: the mode, the costs the model gave the two modes, and for field-wise mode, why.

func (*Package) Generate

func (p *Package) Generate(typeName string) ([]byte, error)

Generate returns the gofmt-ed source of a file declaring `func (r *T) Reset()` for the struct type named typeName.

func (*Package) Name

func (p *Package) Name() string

Name is the package name, as the generated file declares it.

type Result added in v1.2.0

type Result struct {
	// Source is the generated file, as Generate returns it.
	Source []byte
	// Hints say why Reset is field by field and how it could be one
	// assignment instead. Empty when Bulk.
	Hints []string
	// BulkCost and FieldwiseCost are the model's estimates for the two modes,
	// in units of one field store; the cheaper one is generated.
	BulkCost      int
	FieldwiseCost int
	// Bulk reports whether Reset is one assignment with the kept fields
	// restored after it (true), or one statement per field (false).
	Bulk bool
}

Result is what Analyze found out about a type.

Directories

Path Synopsis
cmd
goreset command
Command goreset writes a Reset method for the struct types named by -type into <type>_reset.gen.go in the current package.
Command goreset writes a Reset method for the struct types named by -type into <type>_reset.gen.go in the current package.
internal
cases/bulk
Package bulk is a goreset test case: a struct without locks and with enough plain fields for the cost model to pick one assignment with the kept fields restored after it; every tag is exercised in that mode.
Package bulk is a goreset test case: a struct without locks and with enough plain fields for the cost model to pick one assignment with the kept fields restored after it; every tag is exercised in that mode.
cases/consts
Package consts holds a value a reset tag refers to through import=.
Package consts holds a value a reset tag refers to through import=.
cases/fieldwise
Package fieldwise is a goreset test case: a struct that cannot be reset in one assignment (it holds a lock and a big value with its own Reset), so every field is reset on its own.
Package fieldwise is a goreset test case: a struct that cannot be reset in one assignment (it holds a lock and a big value with its own Reset), so every field is reset on its own.
cases/wide
Package wide is a goreset test case: no locks, but the cost model picks field-wise mode — slices cost a keep and a restore each around one assignment, and there is little else for the assignment to zero.
Package wide is a goreset test case: no locks, but the cost model picks field-wise mode — slices cost a keep and a restore each around one assignment, and there is little else for the assignment to zero.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL