diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-04-17 00:50:17 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-04-17 01:02:13 +0900 |
| commit | 0ff7ab915b8532f4d025ea1e6376b82cfa765c90 (patch) | |
| tree | a263bea4f030115fb686bafe87aaa50833a63d1f /internal/pkg/ir.go | |
| parent | 823575acaccfb34d908dad2ae14551fa425d7431 (diff) | |
internal/pkg: move IR primitives out of cache
These are memory management and caching primitives. Having them as part of Cache is cumbersome and requires a temporary directory that is never used. This change isolates them from Cache to enable independent use.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/pkg/ir.go')
| -rw-r--r-- | internal/pkg/ir.go | 69 |
1 files changed, 46 insertions, 23 deletions
diff --git a/internal/pkg/ir.go b/internal/pkg/ir.go index 21ba8edc..41e75845 100644 --- a/internal/pkg/ir.go +++ b/internal/pkg/ir.go @@ -3,7 +3,6 @@ package pkg import ( "bufio" "bytes" - "context" "crypto/sha512" "encoding/binary" "errors" @@ -11,6 +10,7 @@ import ( "io" "slices" "strconv" + "sync" "syscall" "unique" "unsafe" @@ -39,22 +39,45 @@ func panicToError(errP *error) { } } +// irCache implements [IRCache]. +type irCache struct { + // Artifact to [unique.Handle] of identifier cache. + artifact sync.Map + // Identifier free list, must not be accessed directly. + identPool sync.Pool +} + +// zeroIRCache returns the initialised value of irCache. +func zeroIRCache() irCache { + return irCache{ + identPool: sync.Pool{New: func() any { return new(extIdent) }}, + } +} + +// IRCache provides memory management and caching primitives for IR and +// identifier operations against [Artifact] implementations. +// +// The zero value is not safe for use. +type IRCache struct{ irCache } + +// NewIR returns the address of a new [IRCache]. +func NewIR() *IRCache { + return &IRCache{zeroIRCache()} +} + // IContext is passed to [Artifact.Params] and provides methods for writing // values to the IR writer. It does not expose the underlying [io.Writer]. // // IContext is valid until [Artifact.Params] returns. type IContext struct { - // Address of underlying [Cache], should be zeroed or made unusable after + // Address of underlying irCache, should be zeroed or made unusable after // [Artifact.Params] returns and must not be exposed directly. - cache *Cache + ic *irCache // Written to by various methods, should be zeroed after [Artifact.Params] // returns and must not be exposed directly. w io.Writer } -// Unwrap returns the underlying [context.Context]. -func (i *IContext) Unwrap() context.Context { return i.cache.ctx } - // irZero is a zero IR word. var irZero [wordSize]byte @@ -136,11 +159,11 @@ func (i *IContext) mustWrite(p []byte) { // WriteIdent is not defined for an [Artifact] not part of the slice returned by // [Artifact.Dependencies]. func (i *IContext) WriteIdent(a Artifact) { - buf := i.cache.getIdentBuf() - defer i.cache.putIdentBuf(buf) + buf := i.ic.getIdentBuf() + defer i.ic.putIdentBuf(buf) IRKindIdent.encodeHeader(0).put(buf[:]) - *(*ID)(buf[wordSize:]) = i.cache.Ident(a).Value() + *(*ID)(buf[wordSize:]) = i.ic.Ident(a).Value() i.mustWrite(buf[:]) } @@ -183,19 +206,19 @@ func (i *IContext) WriteString(s string) { // Encode writes a deterministic, efficient representation of a to w and returns // the first non-nil error encountered while writing to w. -func (c *Cache) Encode(w io.Writer, a Artifact) (err error) { +func (ic *irCache) Encode(w io.Writer, a Artifact) (err error) { deps := a.Dependencies() idents := make([]*extIdent, len(deps)) for i, d := range deps { - dbuf, did := c.unsafeIdent(d, true) + dbuf, did := ic.unsafeIdent(d, true) if dbuf == nil { - dbuf = c.getIdentBuf() + dbuf = ic.getIdentBuf() binary.LittleEndian.PutUint64(dbuf[:], uint64(d.Kind())) *(*ID)(dbuf[wordSize:]) = did.Value() } else { - c.storeIdent(d, dbuf) + ic.storeIdent(d, dbuf) } - defer c.putIdentBuf(dbuf) + defer ic.putIdentBuf(dbuf) idents[i] = dbuf } slices.SortFunc(idents, func(a, b *extIdent) int { @@ -221,10 +244,10 @@ func (c *Cache) Encode(w io.Writer, a Artifact) (err error) { } func() { - i := IContext{c, w} + i := IContext{ic, w} defer panicToError(&err) - defer func() { i.cache, i.w = nil, nil }() + defer func() { i.ic, i.w = nil, nil }() a.Params(&i) }() @@ -233,7 +256,7 @@ func (c *Cache) Encode(w io.Writer, a Artifact) (err error) { } var f IREndFlag - kcBuf := c.getIdentBuf() + kcBuf := ic.getIdentBuf() sz := wordSize if kc, ok := a.(KnownChecksum); ok { f |= IREndKnownChecksum @@ -243,13 +266,13 @@ func (c *Cache) Encode(w io.Writer, a Artifact) (err error) { IRKindEnd.encodeHeader(uint32(f)).put(kcBuf[:]) _, err = w.Write(kcBuf[:sz]) - c.putIdentBuf(kcBuf) + ic.putIdentBuf(kcBuf) return } // encodeAll implements EncodeAll by recursively encoding dependencies and // performs deduplication by value via the encoded map. -func (c *Cache) encodeAll( +func (ic *irCache) encodeAll( w io.Writer, a Artifact, encoded map[Artifact]struct{}, @@ -259,13 +282,13 @@ func (c *Cache) encodeAll( } for _, d := range a.Dependencies() { - if err = c.encodeAll(w, d, encoded); err != nil { + if err = ic.encodeAll(w, d, encoded); err != nil { return } } encoded[a] = struct{}{} - return c.Encode(w, a) + return ic.Encode(w, a) } // EncodeAll writes a self-describing IR stream of a to w and returns the first @@ -283,8 +306,8 @@ func (c *Cache) encodeAll( // the ident cache, nor does it contribute identifiers it computes back to the // ident cache. Because of this, multiple invocations of EncodeAll will have // similar cost and does not amortise when combined with a call to Cure. -func (c *Cache) EncodeAll(w io.Writer, a Artifact) error { - return c.encodeAll(w, a, make(map[Artifact]struct{})) +func (ic *irCache) EncodeAll(w io.Writer, a Artifact) error { + return ic.encodeAll(w, a, make(map[Artifact]struct{})) } // ErrRemainingIR is returned for a [IRReadFunc] that failed to call |
