hst: remove enablement json adapter

The go116 behaviour of built-in new function makes this cleaner.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-04-10 20:39:06 +09:00
parent c33a6a5b7e
commit 560cb626a1
26 changed files with 149 additions and 130 deletions

View File

@@ -24,7 +24,7 @@ func entryEncode(w io.Writer, s *hst.State) error {
}
// entryDecodeHeader calls entryReadHeader, returning [hst.AppError] for a non-nil error.
func entryDecodeHeader(r io.Reader) (hst.Enablement, error) {
func entryDecodeHeader(r io.Reader) (hst.Enablements, error) {
if et, err := entryReadHeader(r); err != nil {
return 0, &hst.AppError{Step: "decode state header", Err: err}
} else {
@@ -32,11 +32,11 @@ func entryDecodeHeader(r io.Reader) (hst.Enablement, error) {
}
}
// entryDecode decodes [hst.State] from [io.Reader] and stores the result in the value pointed to by p.
// entryDecode validates the embedded [hst.Config] value.
// entryDecode decodes [hst.State] from [io.Reader] and stores the result in the
// value pointed to by p. entryDecode validates the embedded [hst.Config] value.
//
// A non-nil error returned by entryDecode is of type [hst.AppError].
func entryDecode(r io.Reader, p *hst.State) (hst.Enablement, error) {
func entryDecode(r io.Reader, p *hst.State) (hst.Enablements, error) {
if et, err := entryDecodeHeader(r); err != nil {
return et, err
} else if err = gob.NewDecoder(r).Decode(&p); err != nil {
@@ -45,7 +45,10 @@ func entryDecode(r io.Reader, p *hst.State) (hst.Enablement, error) {
return et, err
} else if p.Enablements.Unwrap() != et {
return et, &hst.AppError{Step: "validate state enablement", Err: os.ErrInvalid,
Msg: fmt.Sprintf("state entry %s has unexpected enablement byte %#x, %#x", p.ID.String(), byte(p.Enablements.Unwrap()), byte(et))}
Msg: fmt.Sprintf(
"state entry %s has unexpected enablement byte %#x, %#x",
p.ID.String(), byte(p.Enablements.Unwrap()), byte(et),
)}
} else {
return et, nil
}

View File

@@ -14,22 +14,25 @@ import (
const (
// entryHeaderMagic are magic bytes at the beginning of the state entry file.
entryHeaderMagic = "\x00\xff\xca\xfe"
// entryHeaderRevision follows entryHeaderMagic and is incremented for revisions of the format.
// entryHeaderRevision follows entryHeaderMagic and is incremented for
// revisions of the format.
entryHeaderRevision = "\x00\x00"
// entryHeaderSize is the fixed size of the header in bytes, including the enablement byte and its complement.
// entryHeaderSize is the fixed size of the header in bytes, including the
// enablement byte and its complement.
entryHeaderSize = len(entryHeaderMagic+entryHeaderRevision) + 2
)
// entryHeaderEncode encodes a state entry header for a [hst.Enablement] byte.
func entryHeaderEncode(et hst.Enablement) *[entryHeaderSize]byte {
// entryHeaderEncode encodes a state entry header for a [hst.Enablements] byte.
func entryHeaderEncode(et hst.Enablements) *[entryHeaderSize]byte {
data := [entryHeaderSize]byte([]byte(
entryHeaderMagic + entryHeaderRevision + string([]hst.Enablement{et, ^et}),
entryHeaderMagic + entryHeaderRevision + string([]hst.Enablements{et, ^et}),
))
return &data
}
// entryHeaderDecode validates a state entry header and returns the [hst.Enablement] byte.
func entryHeaderDecode(data *[entryHeaderSize]byte) (hst.Enablement, error) {
// entryHeaderDecode validates a state entry header and returns the
// [hst.Enablements] byte.
func entryHeaderDecode(data *[entryHeaderSize]byte) (hst.Enablements, error) {
if magic := data[:len(entryHeaderMagic)]; string(magic) != entryHeaderMagic {
return 0, errors.New("invalid header " + hex.EncodeToString(magic))
}
@@ -41,7 +44,7 @@ func entryHeaderDecode(data *[entryHeaderSize]byte) (hst.Enablement, error) {
if et != ^data[len(entryHeaderMagic+entryHeaderRevision)+1] {
return 0, errors.New("header enablement value is inconsistent")
}
return hst.Enablement(et), nil
return hst.Enablements(et), nil
}
// EntrySizeError is returned for a file too small to hold a state entry header.
@@ -68,8 +71,8 @@ func entryCheckFile(fi os.FileInfo) error {
return nil
}
// entryReadHeader reads [hst.Enablement] from an [io.Reader].
func entryReadHeader(r io.Reader) (hst.Enablement, error) {
// entryReadHeader reads [hst.Enablements] from an [io.Reader].
func entryReadHeader(r io.Reader) (hst.Enablements, error) {
var data [entryHeaderSize]byte
if n, err := r.Read(data[:]); err != nil {
return 0, err
@@ -79,8 +82,8 @@ func entryReadHeader(r io.Reader) (hst.Enablement, error) {
return entryHeaderDecode(&data)
}
// entryWriteHeader writes [hst.Enablement] header to an [io.Writer].
func entryWriteHeader(w io.Writer, et hst.Enablement) error {
// entryWriteHeader writes [hst.Enablements] header to an [io.Writer].
func entryWriteHeader(w io.Writer, et hst.Enablements) error {
_, err := w.Write(entryHeaderEncode(et)[:])
return err
}

View File

@@ -20,7 +20,7 @@ func TestEntryHeader(t *testing.T) {
testCases := []struct {
name string
data [entryHeaderSize]byte
et hst.Enablement
et hst.Enablements
err error
}{
{"complement mismatch", [entryHeaderSize]byte{0x00, 0xff, 0xca, 0xfe, 0x00, 0x00,

View File

@@ -14,6 +14,7 @@ import (
)
// EntryHandle is a handle on a state entry retrieved from a [Handle].
//
// Must only be used while its parent [Handle.Lock] is held.
type EntryHandle struct {
// Error returned while decoding pathname.
@@ -27,6 +28,7 @@ type EntryHandle struct {
}
// open opens the underlying state entry file.
//
// A non-nil error returned by open is of type [hst.AppError].
func (eh *EntryHandle) open(flag int, perm os.FileMode) (*os.File, error) {
if eh.DecodeErr != nil {
@@ -41,6 +43,7 @@ func (eh *EntryHandle) open(flag int, perm os.FileMode) (*os.File, error) {
}
// Destroy removes the underlying state entry.
//
// A non-nil error returned by Destroy is of type [hst.AppError].
func (eh *EntryHandle) Destroy() error {
// destroy does not go through open
@@ -55,8 +58,10 @@ func (eh *EntryHandle) Destroy() error {
}
// save encodes [hst.State] and writes it to the underlying file.
//
// An error is returned if a file already exists with the same identifier.
// save does not validate the embedded [hst.Config].
//
// A non-nil error returned by save is of type [hst.AppError].
func (eh *EntryHandle) save(state *hst.State) error {
f, err := eh.open(os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
@@ -71,17 +76,19 @@ func (eh *EntryHandle) save(state *hst.State) error {
return err
}
// Load loads and validates the state entry header, and returns the [hst.Enablement] byte.
// for a non-nil v, the full state payload is decoded and stored in the value pointed to by v.
// Load validates the embedded [hst.Config] value.
// A non-nil error returned by Load is of type [hst.AppError].
func (eh *EntryHandle) Load(v *hst.State) (hst.Enablement, error) {
// Load loads and validates the state entry header, and returns the
// [hst.Enablements] byte. For a non-nil v, the full state payload is decoded
// and stored in the value pointed to by v.
//
// Load validates the embedded [hst.Config] value. A non-nil error returned by
// Load is of type [hst.AppError].
func (eh *EntryHandle) Load(v *hst.State) (hst.Enablements, error) {
f, err := eh.open(os.O_RDONLY, 0)
if err != nil {
return 0, err
}
var et hst.Enablement
var et hst.Enablements
if v != nil {
et, err = entryDecode(f, v)
if err == nil && v.ID != eh.ID {
@@ -99,6 +106,7 @@ func (eh *EntryHandle) Load(v *hst.State) (hst.Enablement, error) {
}
// Handle is a handle on a [Store] segment.
//
// Initialised by [Store.Handle].
type Handle struct {
// Identity of instances tracked by this segment.
@@ -113,8 +121,9 @@ type Handle struct {
mu sync.Mutex
}
// Lock attempts to acquire a lock on [Handle].
// If successful, Lock returns a non-nil unlock function.
// Lock attempts to acquire a lock on [Handle]. If successful, Lock returns a
// non-nil unlock function.
//
// A non-nil error returned by Lock is of type [hst.AppError].
func (h *Handle) Lock() (unlock func(), err error) {
if unlock, err = h.fileMu.Lock(); err != nil {
@@ -123,20 +132,24 @@ func (h *Handle) Lock() (unlock func(), err error) {
return
}
// Save attempts to save [hst.State] as a segment entry, and returns its [EntryHandle].
// Must be called while holding [Handle.Lock].
// Save attempts to save [hst.State] as a segment entry, and returns its
// [EntryHandle]. Must be called while holding [Handle.Lock].
//
// An error is returned if an entry already exists with the same identifier.
// Save does not validate the embedded [hst.Config].
//
// A non-nil error returned by Save is of type [hst.AppError].
func (h *Handle) Save(state *hst.State) (*EntryHandle, error) {
eh := EntryHandle{nil, h.Path.Append(state.ID.String()), state.ID}
return &eh, eh.save(state)
}
// Entries returns an iterator over all [EntryHandle] held in this segment.
// Must be called while holding [Handle.Lock].
// A non-nil error attached to a [EntryHandle] indicates a malformed identifier and is of type [hst.AppError].
// A non-nil error returned by Entries is of type [hst.AppError].
// Entries returns an iterator over all [EntryHandle] held in this segment. Must
// be called while holding [Handle.Lock].
//
// A non-nil error attached to a [EntryHandle] indicates a malformed identifier
// and is of type [hst.AppError]. A non-nil error returned by Entries is of type
// [hst.AppError].
func (h *Handle) Entries() (iter.Seq[*EntryHandle], int, error) {
// for error reporting
const step = "read store segment entries"

View File

@@ -21,7 +21,7 @@ import (
// Made available here for direct validation of state entry files.
//
//go:linkname entryDecode hakurei.app/internal/store.entryDecode
func entryDecode(r io.Reader, p *hst.State) (hst.Enablement, error)
func entryDecode(r io.Reader, p *hst.State) (hst.Enablements, error)
// Made available here for direct access to known segment handles.
//