internal/pkg: compute identifier from deps
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m31s
Test / Hakurei (push) Successful in 3m34s
Test / ShareFS (push) Successful in 3m40s
Test / Hpkg (push) Successful in 4m21s
Test / Sandbox (race detector) (push) Successful in 4m51s
Test / Hakurei (race detector) (push) Successful in 5m50s
Test / Flake checks (push) Successful in 1m46s

This provides infrastructure for computing a deterministic identifier based on current artifact kind, opaque parameters data, and optional dependency kind and identifiers.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-01-03 21:24:50 +09:00
parent deda16da38
commit 8ad9909065
3 changed files with 84 additions and 0 deletions

View File

@@ -2,11 +2,14 @@
package pkg
import (
"bytes"
"crypto/sha512"
"encoding/base64"
"encoding/binary"
"errors"
"io"
"os"
"slices"
"sync"
"hakurei.app/container/check"
@@ -39,6 +42,11 @@ func MustDecode(s string) (checksum Checksum) {
// deterministically but might not currently be available in memory or on the
// filesystem.
type Artifact interface {
// Kind returns the [Kind] of artifact. This is usually unique to the
// concrete type but two functionally identical implementations of
// [Artifact] is allowed to return the same [Kind] value.
Kind() Kind
// ID returns a globally unique identifier referring to the current
// [Artifact]. This value must be known ahead of time and guaranteed to be
// unique without having obtained the full contents of the [Artifact].
@@ -71,6 +79,39 @@ type File interface {
Artifact
}
// Kind corresponds to the concrete type of [Artifact] and is used to create
// identifier for an [Artifact] with dependencies.
type Kind uint64
const (
// KindHTTP is the kind of [Artifact] returned by [Cache.NewHTTP].
KindHTTP Kind = iota
KindTar
)
// Ident returns a deterministic identifier for the supplied params and
// dependencies. The caller is responsible for ensuring params uniquely and
// deterministically describes the current [Artifact].
func (k Kind) Ident(params []byte, deps ...Artifact) ID {
type extIdent [len(ID{}) + wordSize]byte
identifiers := make([]extIdent, len(deps))
for i, a := range deps {
id := a.ID()
copy(identifiers[i][wordSize:], id[:])
binary.LittleEndian.PutUint64(identifiers[i][:], uint64(a.Kind()))
}
slices.SortFunc(identifiers, func(a, b extIdent) int { return bytes.Compare(a[:], b[:]) })
slices.Compact(identifiers)
h := sha512.New384()
h.Write(binary.LittleEndian.AppendUint64(nil, uint64(k)))
h.Write(params)
for _, e := range identifiers {
h.Write(e[:])
}
return ID(h.Sum(nil))
}
const (
// dirIdentifier is the directory name appended to Cache.base for storing
// artifacts named after their [ID].