Some checks are pending
CI / test (push) Waiting to run
CI / native-build (macos-latest) (push) Waiting to run
CI / native-build (ubuntu-latest) (push) Waiting to run
CI / native-build (windows-latest) (push) Waiting to run
CI / native-msrv (macos-latest) (push) Waiting to run
CI / native-msrv (ubuntu-latest) (push) Waiting to run
CI / native-msrv (windows-latest) (push) Waiting to run
523 lines
25 KiB
EmacsLisp
523 lines
25 KiB
EmacsLisp
;;; ebox-state-contract.el --- Ebox retained-state ownership contract -*- lexical-binding: t; -*-
|
|
|
|
;; SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
;;; Commentary:
|
|
|
|
;; Records the M2a target ownership classification for retained Ebox state and
|
|
;; the distinct current v1 storage shape. It also offers read-only
|
|
;; compatibility-mirror rebuild probes. This module does not publish a
|
|
;; generation, mutate live runtime state, or select a transaction route.
|
|
|
|
;;; Code:
|
|
|
|
(require 'cl-lib)
|
|
(require 'subr-x)
|
|
|
|
(define-error 'ebox-state-contract-error
|
|
"Invalid Ebox retained-state contract")
|
|
|
|
(defconst ebox-state-contract-categories
|
|
'(generation-fact generation-bound-mutable-authority
|
|
tp-storage-custody compatibility-mirror disposable-cache)
|
|
"Closed set of retained-state categories used by the M2a inventory.")
|
|
|
|
(defconst ebox-state-contract-required-fields
|
|
'(:id :storage :current-contract :target-contract :category :owner
|
|
:mutation-api :generation-binding :rollback :rebuild-proof :cleanup)
|
|
"Fields required on every M2a retained-state inventory record.")
|
|
|
|
(defconst ebox-state-contract--inventory
|
|
'((:id runtime-generation-indexes
|
|
:storage (:source-index ebox-source-index/records
|
|
ebox-source-index/handle-records
|
|
ebox-source-index/host-ref-table
|
|
:root-node :node-table :parent-table
|
|
:runtime-type-count-table :range-ref-table
|
|
:selector-id-table :selector-class-table :selector-type-table
|
|
ebox--region-line-index ebox--render-source-index
|
|
ebox-incremental--candidate-full-source-base-index
|
|
ebox-incremental--previous-source-base-index
|
|
ebox-incremental--source-base-index)
|
|
:current-contract mixed-candidate-and-committed-state
|
|
:target-contract immutable-generation-value
|
|
:category generation-fact
|
|
:owner ebox
|
|
:mutation-api candidate-construction-only
|
|
:generation-binding immutable-generation-token
|
|
:rollback discard-candidate
|
|
:rebuild-proof source-and-runtime-index-rebuild
|
|
:cleanup generation-replacement)
|
|
(:id region-generation-indexes
|
|
:storage (:region-id-set :region-node-table :region-box-count-table
|
|
:region-box-table :layout-snapshots
|
|
:render-owned-text-values ebox--render-owned-text-values)
|
|
:current-contract candidate-state-plus-global-projection
|
|
:target-contract immutable-generation-value
|
|
:category generation-fact
|
|
:owner ebox
|
|
:mutation-api candidate-layout-materialization-only
|
|
:generation-binding immutable-generation-token
|
|
:rollback discard-candidate
|
|
:rebuild-proof layout-and-region-index-rebuild
|
|
:cleanup generation-replacement)
|
|
(:id scroll-membership
|
|
:storage (:scroll-region-ids)
|
|
:current-contract committed-state-membership-list
|
|
:target-contract immutable-generation-value
|
|
:category generation-fact
|
|
:owner ebox
|
|
:mutation-api candidate-layout-materialization-only
|
|
:generation-binding immutable-generation-token
|
|
:rollback discard-candidate
|
|
:rebuild-proof layout-scroll-membership-rebuild
|
|
:cleanup generation-replacement)
|
|
(:id scroll-runtime-authority
|
|
:storage (:scroll-state-table :scroll-offset :scroll-window
|
|
ebox--scroll-global-state
|
|
ebox--smooth-scroll-state-table
|
|
ebox--scroll-idle-prefetch-timers
|
|
ebox--scroll-idle-prefetch-inhibited-buffers)
|
|
:current-contract global-and-state-table-mutable-handles
|
|
:target-contract stable-id-plus-generation-token-authority
|
|
:category generation-bound-mutable-authority
|
|
:owner ebox-scroll
|
|
:mutation-api stable-scroll-id-and-generation-token
|
|
:generation-binding required
|
|
:rollback participant-journal-restores-prior-authority
|
|
:rebuild-proof not-rebuildable-from-cache
|
|
:cleanup cancel-timers-and-retire-generation)
|
|
(:id native-runtime-authority
|
|
:storage (:native-sync-session :native-sync-pending :native-session
|
|
ebox-native-reflow-preparation ebox-native-reflow-session
|
|
ebox-native-reflow-preparation/ready-timer)
|
|
:current-contract candidate-preparation-and-confirmed-session-handles
|
|
:target-contract generation-token-authority
|
|
:category generation-bound-mutable-authority
|
|
:owner ebox-native
|
|
:mutation-api native-candidate-confirm-or-abort
|
|
:generation-binding required
|
|
:rollback abort-candidate-and-keep-confirmed-session
|
|
:rebuild-proof not-rebuildable-from-cache
|
|
:cleanup release-losing-session)
|
|
(:id runtime-prewarm-authority
|
|
:storage (ebox--deferred-render-gc-state
|
|
ebox--deferred-render-gc-timer
|
|
ebox--deferred-render-gc-depth
|
|
ebox--deferred-render-gc-generation
|
|
ebox--reflow-cache-prewarm-timers
|
|
ebox--runtime-prewarm-jobs ebox--runtime-prewarm-timers
|
|
ebox--render-burst-records ebox--render-burst-stack)
|
|
:current-contract buffer-keyed-jobs-and-cleanup-sensitive-timers
|
|
:target-contract generation-revision-guarded-scheduler-authority
|
|
:category generation-bound-mutable-authority
|
|
:owner ebox-runtime-scheduler
|
|
:mutation-api schedule-cancel-and-revision-validate
|
|
:generation-binding buffer-and-runtime-revision
|
|
:rollback cancel-candidate-work
|
|
:rebuild-proof reschedule-from-committed-generation
|
|
:cleanup cancel-timers-jobs-and-gc-lease)
|
|
(:id incremental-batch-authority
|
|
:storage (ebox-incremental--batch-table)
|
|
:current-contract process-buffer-keyed-open-batch-state
|
|
:target-contract generation-token-bound-batch-authority
|
|
:category generation-bound-mutable-authority
|
|
:owner ebox-incremental
|
|
:mutation-api batch-begin-record-flush-or-abort
|
|
:generation-binding captured-base-generation
|
|
:rollback discard-pending-batch
|
|
:rebuild-proof batch-begin-from-committed-generation
|
|
:cleanup batch-end-or-buffer-kill)
|
|
(:id layout-context-port-authority
|
|
:storage (ebox--layout-context-port)
|
|
:current-contract process-global-port-with-dynamic-test-rebinding
|
|
:target-contract immutable-package-bootstrap-port
|
|
:category generation-bound-mutable-authority
|
|
:owner ebox-render-context
|
|
:mutation-api facade-install-and-dynamic-bind
|
|
:generation-binding one-port-captured-per-render-call
|
|
:rollback dynamic-binding-unwind
|
|
:rebuild-proof reinstall-default-surface-port
|
|
:cleanup package-reload-or-binding-unwind)
|
|
(:id identity-allocation-authority
|
|
:storage (ebox--region-id-counter ebox--runtime-node-id-counter)
|
|
:current-contract process-monotonic-identity-counters
|
|
:target-contract allocator-output-copied-into-generation
|
|
:category generation-bound-mutable-authority
|
|
:owner ebox-identity
|
|
:mutation-api next-region-or-runtime-node-id
|
|
:generation-binding allocated-identity-enters-one-candidate
|
|
:rollback allocator-gaps-are-non-authoritative
|
|
:rebuild-proof monotonic-next-allocation
|
|
:cleanup process-lifetime-test-reset-only)
|
|
(:id buffer-surface-runtime-authority
|
|
:storage (ebox-surface--buffer-surface
|
|
ebox-surface--buffer-observer
|
|
ebox-surface--tp-observer
|
|
ebox-surface--observation-contexts
|
|
ebox-surface--context-signals)
|
|
:current-contract buffer-local-mounted-surface-and-observation-handles
|
|
:target-contract generation-revision-bound-surface-authority
|
|
:category generation-bound-mutable-authority
|
|
:owner ebox-surface
|
|
:mutation-api mount-observe-publish-and-unmount
|
|
:generation-binding buffer-surface-and-publication-revision
|
|
:rollback restore-prior-surface-signals-and-observer-bridge
|
|
:rebuild-proof not-rebuildable-from-cache
|
|
:cleanup remove-observers-cancel-contexts-and-unmount)
|
|
(:id tp-client-state-custody
|
|
:storage (tp-surface-client-state)
|
|
:current-contract entire-ebox-state-plist
|
|
:target-contract opaque-generation-correlation-only
|
|
:category tp-storage-custody
|
|
:owner tp
|
|
:mutation-api opaque-ebox-generation-correlation-only
|
|
:generation-binding opaque-correlation
|
|
:rollback tp-restores-client-state
|
|
:rebuild-proof ebox-generation-remains-source-of-truth
|
|
:cleanup tp-surface-unmount)
|
|
(:id buffer-render-state-mirror
|
|
:storage (ebox--buffer-render-state-table)
|
|
:current-contract same-object-alias-of-tp-client-state
|
|
:target-contract one-way-generation-projection
|
|
:category compatibility-mirror
|
|
:owner ebox-compatibility
|
|
:mutation-api project-from-committed-generation
|
|
:generation-binding committed-generation-token
|
|
:rollback participant-restores-prior-projection
|
|
:rebuild-proof project-buffer-mirror-from-committed-states
|
|
:cleanup remove-buffer-entry)
|
|
(:id region-box-lookup-mirror
|
|
:storage (ebox--region-box-table)
|
|
:current-contract participant-maintained-global-projection
|
|
:target-contract rebuildable-generation-projection
|
|
:category compatibility-mirror
|
|
:owner ebox-compatibility
|
|
:mutation-api project-from-committed-generation
|
|
:generation-binding committed-generation-token
|
|
:rollback participant-restores-prior-projection
|
|
:rebuild-proof project-region-mirror-from-generation-indexes
|
|
:cleanup remove-retired-generation-entries)
|
|
(:id font-cache
|
|
:storage (ebox-font--cache)
|
|
:current-contract process-display-capability-cache
|
|
:target-contract discardable-derived-values
|
|
:category disposable-cache
|
|
:owner ebox-font
|
|
:mutation-api cache-fill-and-evict
|
|
:generation-binding display-capability-signature
|
|
:rollback discard
|
|
:rebuild-proof resolve-font-fact-again
|
|
:cleanup ebox-font-clear-cache)
|
|
(:id derived-caches
|
|
:storage (ebox--box-content-render-cache ebox--char-width-cache
|
|
ebox--display-signature-cache ebox--face-height-width-cache
|
|
ebox--physical-memory-bytes
|
|
ebox--flex-content-min-width-table
|
|
ebox--flex-sized-render-observation-table
|
|
ebox--flex-slot-safety-cache ebox--layout-fragments-table
|
|
ebox--node-region-ids-cache ebox--patchable-owner-cache
|
|
ebox--render-cache-entry-side-effects-table
|
|
ebox--render-cache-ring-table
|
|
ebox--render-cache-scroll-state-region-ids
|
|
ebox--render-cache-scroll-state-retained-cost-cache
|
|
ebox--render-cache-signature-cache ebox--render-cache-table
|
|
ebox--render-output-provenance-table
|
|
ebox--render-recached-source-node-cache
|
|
ebox--render-root-cache-table-table
|
|
ebox--render-string-max-pixel-width-cache
|
|
ebox--render-string-pixel-width-cache
|
|
ebox--rendered-uniform-width-table ebox--runtime-ancestor-cache
|
|
ebox--scroll-window-cached-state ebox--space-pixel-cache
|
|
ebox--string-pixel-width-cache
|
|
ebox--string-pixel-width-cache-ring
|
|
ebox--viewport-dependent-node-ids-cache
|
|
ebox--viewport-dependent-subtree-cache
|
|
ebox--viewport-height-dependent-subtree-cache
|
|
ebox--window-line-prewarmer-table
|
|
ebox--window-line-renderer-table
|
|
ebox-cache--buffer-report-table ebox-cache--registry
|
|
ebox-canonical--declaration-fact-cache
|
|
ebox-incremental--allocated-slot-proof-cache
|
|
ebox-incremental--candidate-path-copy-origin-table
|
|
ebox-incremental--candidate-proof-node-table
|
|
ebox-native-reflow--source-cluster-cache
|
|
ebox-style--box-engine-longhand-cache
|
|
ebox-style--closed-computed-cache
|
|
ebox-style--closed-inheritance-cache
|
|
ebox-style--computed-snapshot-cache
|
|
ebox-style--declaration-cache ebox-style--property-index
|
|
ebox-style--text-engine-longhand-cache
|
|
ebox-surface--inline-style-value-cache
|
|
ebox-surface--paint-node-chain-cache
|
|
ebox-surface--paint-node-depth-cache
|
|
ebox-surface--scroll-line-fragment-cache)
|
|
:current-contract process-or-render-local-derived-values
|
|
:target-contract discardable-derived-values
|
|
:category disposable-cache
|
|
:owner ebox-cache
|
|
:mutation-api cache-fill-and-evict
|
|
:generation-binding cache-key-or-display-signature
|
|
:rollback discard
|
|
:rebuild-proof recompute-with-identical-semantic-result
|
|
:cleanup bounded-eviction-or-clear))
|
|
"M2a retained-state inventory.
|
|
|
|
The records classify every state family named by the architecture plan. They
|
|
do not claim that later M2a target storage is already active. The inventory is
|
|
fail-closed: adding a retained-state family requires adding a complete record
|
|
rather than relying on an implicit default.")
|
|
|
|
(defconst ebox-state-contract-source-scan-exclusions
|
|
'((:symbol ebox--defer-scroll-content-index
|
|
:reason dynamic-boolean-or-index-mode
|
|
:evidence value-is-never-a-container)
|
|
(:symbol ebox--string-pixel-width-cache-ring-index
|
|
:reason numeric-eviction-cursor
|
|
:evidence value-is-an-integer-ring-position)
|
|
(:symbol ebox-native-reflow--compile-property-template-ids
|
|
:reason dynamically-bound-compile-local-scratch
|
|
:evidence let-bound-per-compile-and-never-committed)
|
|
(:symbol ebox-native-reflow--compile-property-template-index
|
|
:reason dynamically-bound-compile-local-scratch
|
|
:evidence let-bound-per-compile-and-published-only-through-session-root)
|
|
(:symbol ebox-native-reflow--compile-style-index
|
|
:reason dynamically-bound-compile-local-scratch
|
|
:evidence let-bound-per-compile-and-published-only-through-session-root)
|
|
(:symbol ebox--render-source-generations
|
|
:reason dynamically-bound-render-proof-input
|
|
:evidence let-bound-to-candidate-and-prior-generations-for-one-plan)
|
|
(:symbol ebox--render-source-states
|
|
:reason dynamically-bound-render-proof-input
|
|
:evidence let-bound-to-candidate-and-prior-states-for-one-plan)
|
|
(:symbol ebox-tree--incoming-source-indexes
|
|
:reason dynamically-bound-tree-build-input
|
|
:evidence let-bound-for-one-persistent-tree-construction)
|
|
(:symbol ebox--rebuilt-scroll-state-region-ids
|
|
:reason dynamically-bound-render-observation
|
|
:evidence collects-rebuilt-scroll-ids-for-one-transaction)
|
|
(:symbol ebox-incremental--buffer-render-state-override
|
|
:reason dynamically-bound-candidate-view
|
|
:evidence let-bound-to-one-buffer-and-candidate-state)
|
|
(:symbol ebox-incremental--candidate-base-state
|
|
:reason dynamically-bound-candidate-view
|
|
:evidence let-bound-to-one-buffer-and-published-base-state)
|
|
(:symbol ebox-incremental--candidate-incoming-source-indexes
|
|
:reason dynamically-bound-candidate-input
|
|
:evidence let-bound-for-one-logical-candidate-preparation)
|
|
(:symbol ebox--prepared-root-render
|
|
:reason transaction-local-render-scratch
|
|
:evidence dynamically-bound-around-one-prepared-render)
|
|
(:symbol ebox--scroll-window-prewarm-state
|
|
:reason render-local-prewarm-scratch
|
|
:evidence dynamically-bound-for-one-yielding-prefix-pass)
|
|
(:symbol ebox--scroll-window-render-result
|
|
:reason render-local-result-scratch
|
|
:evidence dynamically-bound-for-one-window-render)
|
|
(:symbol ebox-incremental--candidate-path-copy-trace
|
|
:reason candidate-local-proof-scratch
|
|
:evidence dynamically-bound-during-one-candidate-build)
|
|
(:symbol ebox-incremental--candidate-range-index-deltas
|
|
:reason candidate-local-index-scratch
|
|
:evidence dynamically-bound-during-one-candidate-build)
|
|
(:symbol ebox-native-reflow--compile-property-templates
|
|
:reason dynamically-bound-compile-local-scratch
|
|
:evidence let-bound-per-native-compile)
|
|
(:symbol ebox-native-reflow--compile-styles
|
|
:reason dynamically-bound-compile-local-scratch
|
|
:evidence let-bound-per-native-compile)
|
|
(:symbol ebox--flex-native-size-lines-backend
|
|
:reason optional-backend-capability-not-retained-generation-state
|
|
:evidence function-capability-selected-outside-publication)
|
|
(:symbol ebox-native-reflow--available-p
|
|
:reason process-native-loader-capability
|
|
:evidence adapter-availability-not-render-generation-state)
|
|
(:symbol ebox-native-reflow--load-attempted-p
|
|
:reason process-native-loader-control
|
|
:evidence one-time-loader-guard-not-domain-state)
|
|
(:symbol ebox-native-reflow--load-error
|
|
:reason process-native-loader-diagnostic
|
|
:evidence diagnostic-only-not-publication-authority)
|
|
(:symbol ebox-native-reflow--loaded-module-hash
|
|
:reason process-native-loader-diagnostic
|
|
:evidence loaded-binary-identity-outside-render-generation)
|
|
(:symbol ebox-native-reflow--loaded-module-path
|
|
:reason process-native-loader-diagnostic
|
|
:evidence loaded-binary-location-outside-render-generation)
|
|
(:symbol ebox-native-reflow--build-process
|
|
:reason developer-native-build-handle
|
|
:evidence optional-build-command-outside-render-runtime)
|
|
(:symbol ebox-native-reflow--last-build-report
|
|
:reason developer-native-build-diagnostic
|
|
:evidence optional-build-report-outside-render-runtime)
|
|
(:symbol ebox-native-status--diagnosis
|
|
:reason buffer-local-native-status-diagnostic
|
|
:evidence status-buffer-presentation-only)
|
|
(:symbol ebox-surface--observation-context
|
|
:reason dynamically-bound-publication-observation
|
|
:evidence let-bound-for-one-public-ebox-call)
|
|
(:symbol ebox-fragment-flex-retention-hit-count
|
|
:reason diagnostic-counter
|
|
:evidence test-and-observation-metric-only)
|
|
(:symbol ebox-fragment-flex-retention-rerender-count
|
|
:reason diagnostic-counter
|
|
:evidence test-and-observation-metric-only)
|
|
(:symbol ebox-fragment-flex-retention-store-count
|
|
:reason diagnostic-counter
|
|
:evidence test-and-observation-metric-only)
|
|
(:symbol ebox-native-reflow--flex-geometry-call-count
|
|
:reason diagnostic-counter
|
|
:evidence performance-observation-only)
|
|
(:symbol ebox-scroll-map
|
|
:reason static-ui-keymap
|
|
:evidence command-binding-table-not-render-state))
|
|
"Source-state symbols proven not to hold retained runtime authority.")
|
|
|
|
(defun ebox-state-contract-storage-symbols ()
|
|
"Return all symbol-valued storage identities in the retained inventory."
|
|
(delete-dups
|
|
(cl-mapcan
|
|
(lambda (record)
|
|
(cl-remove-if-not #'symbolp
|
|
(copy-sequence (plist-get record :storage))))
|
|
ebox-state-contract--inventory)))
|
|
|
|
(defun ebox-state-contract-inventory ()
|
|
"Return a detached copy of the retained-state inventory."
|
|
(copy-tree ebox-state-contract--inventory))
|
|
|
|
(defun ebox-state-contract-record (id)
|
|
"Return a detached inventory record identified by ID, or nil."
|
|
(when-let* ((record
|
|
(cl-find id ebox-state-contract--inventory
|
|
:key (lambda (item) (plist-get item :id)))))
|
|
(copy-tree record)))
|
|
|
|
(defun ebox-state-contract-validate ()
|
|
"Validate and return a detached retained-state inventory.
|
|
Signal `ebox-state-contract-error' when a record is incomplete, duplicated, or
|
|
uses a category outside `ebox-state-contract-categories'."
|
|
(let ((seen-ids (make-hash-table :test #'eq))
|
|
(seen-storage (make-hash-table :test #'eq)))
|
|
(dolist (record ebox-state-contract--inventory)
|
|
(dolist (field ebox-state-contract-required-fields)
|
|
(unless (plist-get record field)
|
|
(signal 'ebox-state-contract-error
|
|
(list :missing-field field :record record))))
|
|
(let ((id (plist-get record :id))
|
|
(category (plist-get record :category)))
|
|
(when (gethash id seen-ids)
|
|
(signal 'ebox-state-contract-error
|
|
(list :duplicate-id id)))
|
|
(puthash id t seen-ids)
|
|
(unless (memq category ebox-state-contract-categories)
|
|
(signal 'ebox-state-contract-error
|
|
(list :unknown-category category :id id)))
|
|
(dolist (storage (plist-get record :storage))
|
|
(when (and (symbolp storage) (not (keywordp storage)))
|
|
(when-let* ((prior-id (gethash storage seen-storage)))
|
|
(signal 'ebox-state-contract-error
|
|
(list :duplicate-storage storage
|
|
:first-record prior-id
|
|
:second-record id)))
|
|
(puthash storage id seen-storage)))))
|
|
(ebox-state-contract-inventory)))
|
|
|
|
(defun ebox-state-contract--project-buffer-mirror (entries)
|
|
"Project BUFFER . STATE ENTRIES into a fresh compatibility mirror."
|
|
(let ((table (make-hash-table :test #'eq))
|
|
(missing (make-symbol "missing")))
|
|
(dolist (entry entries)
|
|
(unless (and (consp entry) (bufferp (car entry)) (listp (cdr entry)))
|
|
(signal 'ebox-state-contract-error
|
|
(list :malformed-committed-state-entry entry)))
|
|
(unless (eq (gethash (car entry) table missing) missing)
|
|
(signal 'ebox-state-contract-error
|
|
(list :duplicate-buffer (car entry))))
|
|
(puthash (car entry) (cdr entry) table))
|
|
table))
|
|
|
|
(defun ebox-state-contract--project-region-mirror (entries)
|
|
"Project BUFFER . STATE ENTRIES into a fresh region compatibility mirror."
|
|
(let ((table (make-hash-table :test #'equal))
|
|
(missing (make-symbol "missing")))
|
|
(dolist (entry entries)
|
|
(when-let* ((regions (plist-get (cdr entry) :region-box-table)))
|
|
(unless (hash-table-p regions)
|
|
(signal 'ebox-state-contract-error
|
|
(list :malformed-region-index (car entry))))
|
|
(maphash
|
|
(lambda (region-id box)
|
|
(let ((existing (gethash region-id table missing)))
|
|
(when (and (not (eq existing missing)) (not (eq existing box)))
|
|
(signal 'ebox-state-contract-error
|
|
(list :duplicate-region-id region-id)))
|
|
(puthash region-id box table)))
|
|
regions)))
|
|
table))
|
|
|
|
(defun ebox-state-contract-rebuild-compatibility-mirrors (entries)
|
|
"Return fresh buffer and region mirrors projected from committed ENTRIES.
|
|
|
|
ENTRIES is a list of `(BUFFER . STATE)' pairs. STATE is the current v1 Ebox
|
|
state plist held by TP client-state custody; a later checkpoint may narrow that
|
|
custody to opaque generation correlation. The returned plist contains
|
|
`:buffer-table' and `:region-table'; neither table mutates live Ebox state."
|
|
(list :buffer-table
|
|
(ebox-state-contract--project-buffer-mirror entries)
|
|
:region-table
|
|
(ebox-state-contract--project-region-mirror entries)))
|
|
|
|
(defun ebox-state-contract--hash-diff-report (expected actual)
|
|
"Return deterministic differences and work count for two hash tables."
|
|
(unless (and (hash-table-p expected) (hash-table-p actual))
|
|
(signal 'wrong-type-argument (list 'hash-table-p expected actual)))
|
|
(let ((missing (make-symbol "missing")) differences keys)
|
|
(maphash (lambda (key _value) (push key keys)) expected)
|
|
(maphash (lambda (key _value) (push key keys)) actual)
|
|
(dolist (key (delete-dups keys))
|
|
(let ((left (gethash key expected missing))
|
|
(right (gethash key actual missing)))
|
|
(unless (or (eq left right)
|
|
(and (not (eq left missing))
|
|
(not (eq right missing))
|
|
(equal left right)))
|
|
(push key differences))))
|
|
(list
|
|
:differences
|
|
(sort differences
|
|
(lambda (left right)
|
|
(string< (prin1-to-string left) (prin1-to-string right))))
|
|
:comparisons (length (delete-dups keys)))))
|
|
|
|
(defun ebox-state-contract-probe-compatibility-mirrors
|
|
(entries buffer-mirror region-mirror)
|
|
"Compare live mirrors with fresh projections from committed ENTRIES.
|
|
|
|
BUFFER-MIRROR and REGION-MIRROR are observed compatibility tables. The return
|
|
value is a read-only report with deterministic mismatch lists and a boolean
|
|
`:consistent-p'."
|
|
(let* ((rebuilt
|
|
(ebox-state-contract-rebuild-compatibility-mirrors entries))
|
|
(buffer-report
|
|
(ebox-state-contract--hash-diff-report
|
|
(plist-get rebuilt :buffer-table) buffer-mirror))
|
|
(region-report
|
|
(ebox-state-contract--hash-diff-report
|
|
(plist-get rebuilt :region-table) region-mirror))
|
|
(buffer-differences (plist-get buffer-report :differences))
|
|
(region-differences (plist-get region-report :differences)))
|
|
(list :consistent-p (and (null buffer-differences)
|
|
(null region-differences))
|
|
:buffer-differences buffer-differences
|
|
:region-differences region-differences
|
|
:entry-count (length entries)
|
|
:projected-region-count
|
|
(hash-table-count (plist-get rebuilt :region-table))
|
|
:buffer-comparisons (plist-get buffer-report :comparisons)
|
|
:region-comparisons (plist-get region-report :comparisons))))
|
|
|
|
(provide 'ebox-state-contract)
|
|
|
|
;;; ebox-state-contract.el ends here
|