Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions crates/labcolors-core/contracts/clean-set-srgb8-v1/receipt-v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,39 +83,39 @@
"sha256": "0c8a7bb59464e1ddcace07b01ba24a64f8c9d3aeff547651dc38c276a0fc3322"
},
{
"bytes": 67914,
"bytes": 69967,
"license": "MIT",
"path": "crates/labcolors-core/src/program/attachment/tests.rs",
"role": "point_attachment_tests",
"sha256": "a0838361c5b82c17766af1c8175f9ed2fca9d483a50ebe1aab80ee3ef3690e86"
"sha256": "f1cbd583163a287ac9ea806d748cc789e4fb6f29880432cc97cf77705ad41075"
},
{
"bytes": 162822,
"bytes": 164718,
"license": "MIT",
"path": "crates/labcolors-core/src/program.rs",
"role": "program_facade_source",
"sha256": "62572c59cdc16e0e809d662b11e9d90ea658ef63c70c34962016ee3f39efc20e"
"sha256": "0ebca95215dc6c7fe462cd87b933cf7b67a67ae0b69863601ab5579bcda481d6"
},
{
"bytes": 71522,
"bytes": 71739,
"license": "MIT",
"path": "crates/labcolors-core/src/program_identity.rs",
"role": "program_identity_source",
"sha256": "8f0366079e6fa0006360ab19b0449e622add48fdb08431a02258236e1371d78a"
"sha256": "300b6a313232a625f98ac14d6ceb66cc866f3605354b5566d02d86ea3ecadcab"
},
{
"bytes": 168536,
"bytes": 168664,
"license": "MIT",
"path": "crates/labcolors-core/src/program_session.rs",
"role": "program_source",
"sha256": "8ff19a493171c5437066b8ce489c1925e26f93b4981ad4f1c0ac88461e90eab9"
"sha256": "c5f30b92165d4836f29fa9195885143aadc9a75ee4ca7540674377b4f5ad9a19"
},
{
"bytes": 21546,
"bytes": 21705,
"license": "MIT",
"path": "crates/labcolors-core/src/program_clean_set_tests.rs",
"role": "program_tests",
"sha256": "44c30c5e10494dec4d6865e4597cf9f5e616955eff8279bb606d1f5a9f3ce35f"
"sha256": "bbb4265f0d19f01c5aba71d70c57fbe1846b649bcbd6e944a818c46695dc6373"
},
{
"bytes": 11370,
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2cdb16116e01efd9d76bff544e1b13dac1cc9a59208bf5ad5f42808c1e024cc0 receipt-v1.json
be3f324c6931d7aa967c5941c3b7c841711e36cef75468dea636662a9e7b0a9b receipt-v1.json
14 changes: 10 additions & 4 deletions crates/labcolors-core/src/generic_boundary_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1409,23 +1409,29 @@ fn program_identity_binds_lcs_releases_only_through_lcs_constraint_content() {
"DOMAIN_V2",
"PROGRAM_SCHEMA_V2",
"compile_program_content_identity_v2",
"ProgramContentIdentityV3",
"ContentIdentityV3",
"DOMAIN_V3",
"PROGRAM_SCHEMA_V3",
"compile_program_content_identity_v3",
] {
for (path, source) in identity_sources {
assert!(
!contains_rust_identifier(source, retired),
"the V3 content-address cut must not retain legacy identity symbol `{retired}` in {path}",
"the V4 content-address cut must not retain legacy identity symbol `{retired}` in {path}",
);
}
}
assert!(!PROGRAM_IDENTITY_SOURCE.contains("labcolors.program-content-identity.v1"));
assert!(!PROGRAM_IDENTITY_SOURCE.contains("labcolors.program-content-identity.v2"));
assert!(!PROGRAM_IDENTITY_SOURCE.contains("labcolors.program-content-identity.v3"));
for required in [
"const DOMAIN_V3: &[u8] = b\"labcolors.program-content-identity.v3\\0\";",
"pub(super) const PROGRAM_SCHEMA_V3: u8 = 3;",
"const DOMAIN_V4: &[u8] = b\"labcolors.program-content-identity.v4\\0\";",
"pub(super) const PROGRAM_SCHEMA_V4: u8 = 4;",
] {
assert!(
PROGRAM_IDENTITY_SOURCE.contains(required),
"the V3 content-address type must bind its exact domain and schema tag; missing `{required}`",
"the V4 content-address type must bind its exact domain and schema tag; missing `{required}`",
);
}

Expand Down
104 changes: 74 additions & 30 deletions crates/labcolors-core/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//! [`CertificateV1::Verified`] хранит выбранное состояние, все клетки
//! доказательства и сертифицированные Paint outputs. [`CertificateV1::Conflict`]
//! хранит исчерпывающий конфликт по всем рассмотренным состояниям.
//! [`ContentIdentityV3`] идентифицирует каноническое содержание, но не даёт
//! [`ContentIdentityV4`] идентифицирует каноническое содержание, но не даёт
//! полномочий живого [`OwnerV1`].

#![forbid(unreachable_pub)]
Expand All @@ -35,8 +35,10 @@ pub(crate) mod attachment;
use core::iter::FusedIterator;

use crate::Srgb8;
use crate::appearance::{OccurrenceId, OpacityInputId, PaintId, SurfaceId, SurfaceInputPortId};
use crate::composition::CompositionProfileV1;
use crate::appearance::{
EncodedPointPaintValueV1, OccurrenceId, OpacityInputId, PaintId, SurfaceId, SurfaceInputPortId,
};
use crate::composition::{AdmittedOpacityV1, CompositionProfileV1, OpacityAdmissionErrorV1};
use crate::constraints::{
ApplicableWcag22EvaluationErrorV1, ExactSrgb8IdentityV1, ProgramVisiblePointBindingV1,
ProgramVisiblePointPassEvidence, ProgramVisiblePointViolationEvidence, Wcag22Srgb8V1,
Expand Down Expand Up @@ -64,7 +66,7 @@ use crate::program_session::{
PointPresentationRootV1, PointPresentationTargetV1, PresentationRootId, ProgramCompileError,
ProgramConflictV1, ProgramConstraintCellV1, ProgramConstraintPassEvidenceV1,
ProgramConstraintResultV1, ProgramConstraintSubjectV1, ProgramConstraintViolationEvidenceV1,
ProgramContentIdentityV3, ProgramPaintOutputV1, ProgramSessionEvaluationError,
ProgramContentIdentityV4, ProgramPaintOutputV1, ProgramSessionEvaluationError,
ProgramSessionInstantiateError, ProgramSessionPlan, ProgramVerifiedV1, Source, SourceId,
Surface, Target, TargetCandidateChoiceV1, TargetCandidateId,
TargetCandidateV1 as CoreTargetCandidateV1, TargetId,
Expand Down Expand Up @@ -213,9 +215,51 @@ projected_id!(
pub(crate) struct TargetCandidateV1(CoreTargetCandidateV1);

impl TargetCandidateV1 {
/// Связывает непрозрачный ID кандидата с конкретным encoded sRGB8 сигналом.
pub(crate) const fn new(id: TargetCandidateIdV1, source: Srgb8) -> Self {
Self(CoreTargetCandidateV1::from_srgb8(id.into_core(), source))
/// Связывает непрозрачный ID с одним неделимым физическим Paint value.
pub(crate) const fn new(id: TargetCandidateIdV1, value: PaintValueV1) -> Self {
Self(CoreTargetCandidateV1::new(id.into_core(), value.0))
}
}

/// Admission-ошибка атомарного значения конечного Paint-кандидата.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PaintValueErrorV1 {
/// Straight alpha не является конечным binary64.
NonFiniteOpacity,
/// Straight alpha находится вне замкнутого `[0, 1]`.
OpacityOutsideUnitInterval,
}

/// Одно неделимое encoded-sRGB8 source + straight-alpha значение Paint.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct PaintValueV1(EncodedPointPaintValueV1);

impl PaintValueV1 {
/// Принимает только физически определённый straight alpha и канонизирует
/// оба знака нуля, чтобы одно значение имело одно identity-представление.
pub(crate) fn try_new(source: Srgb8, opacity: f64) -> Result<Self, PaintValueErrorV1> {
let opacity = AdmittedOpacityV1::new(opacity).map_err(|error| match error {
OpacityAdmissionErrorV1::NonFinite => PaintValueErrorV1::NonFiniteOpacity,
OpacityAdmissionErrorV1::OutsideUnitInterval => {
PaintValueErrorV1::OpacityOutsideUnitInterval
}
})?;
Ok(Self(EncodedPointPaintValueV1::from_admitted(
source, opacity,
)))
}

/// Явный shorthand для распространённого непрозрачного значения.
pub(crate) const fn opaque(source: Srgb8) -> Self {
Self(EncodedPointPaintValueV1::opaque(source))
}

pub(crate) const fn source(self) -> Srgb8 {
self.0.source()
}

pub(crate) const fn opacity(self) -> f64 {
self.0.opacity().value()
}
}

Expand Down Expand Up @@ -416,8 +460,8 @@ pub(crate) enum CompileErrorKindV1 {
DuplicateTarget,
/// В одной цели повторно объявлен ID кандидата.
DuplicateTargetCandidate,
/// Два кандидата одной цели задают одинаковый сигнал.
DuplicateTargetCandidateSignal,
/// Два кандидата одной цели задают одинаковое атомарное Paint value.
DuplicateTargetCandidateValue,
/// Повторно объявлен вход прозрачности.
DuplicateOpacityInput,
/// Повторно объявлен входной порт поверхности.
Expand Down Expand Up @@ -831,16 +875,16 @@ pub(crate) enum CompileErrorV1 {
/// Повторный кандидат.
candidate: TargetCandidateIdV1,
},
/// Два кандидата одной цели имеют одинаковый физический сигнал.
DuplicateTargetCandidateSignal {
/// Два кандидата одной цели имеют одинаковое физическое Paint value.
DuplicateTargetCandidateValue {
/// Цель кандидатов.
target: TargetIdV1,
/// Первый кандидат.
first: TargetCandidateIdV1,
/// Повторный кандидат.
duplicate: TargetCandidateIdV1,
/// Совпавший encoded sRGB8 сигнал.
encoded_srgb8: Srgb8,
/// Совпавшая неделимая пара source + straight alpha.
value: PaintValueV1,
},
/// Конечная цель не участвует ни в одном ограничении.
UnconstrainedTarget {
Expand Down Expand Up @@ -983,7 +1027,7 @@ impl CompileErrorV1 {
Self::OpacityOutOfDomain { .. } => Kind::OpacityOutOfDomain,
Self::EmptyTargetDomain { .. } => Kind::EmptyTargetDomain,
Self::DuplicateTargetCandidate { .. } => Kind::DuplicateTargetCandidate,
Self::DuplicateTargetCandidateSignal { .. } => Kind::DuplicateTargetCandidateSignal,
Self::DuplicateTargetCandidateValue { .. } => Kind::DuplicateTargetCandidateValue,
Self::UnconstrainedTarget { .. } => Kind::UnconstrainedTarget,
Self::DisconnectedFiniteTargets => Kind::DisconnectedFiniteTargets,
Self::UnassessedOutput { .. } => Kind::UnassessedOutput,
Expand Down Expand Up @@ -1025,7 +1069,7 @@ impl CompileErrorV1 {
| Self::JointStateUnknownCandidate { target, .. }
| Self::MissingTargetSource { target, .. }
| Self::DuplicateTargetCandidate { target, .. }
| Self::DuplicateTargetCandidateSignal { target, .. } => Some(Handle::Target(*target)),
| Self::DuplicateTargetCandidateValue { target, .. } => Some(Handle::Target(*target)),
Self::DuplicateOpacityInput { input } | Self::OpacityOutOfDomain { input } => {
Some(Handle::OpacityInput(*input))
}
Expand Down Expand Up @@ -1107,7 +1151,7 @@ impl CompileErrorV1 {
| Self::MissingOutputPaint { paint, .. } => Some(Handle::Paint(*paint)),
Self::MissingOccurrenceBackdrop { surface, .. } => Some(Handle::Surface(*surface)),
Self::DuplicateTargetCandidate { candidate, .. }
| Self::DuplicateTargetCandidateSignal {
| Self::DuplicateTargetCandidateValue {
duplicate: candidate,
..
}
Expand Down Expand Up @@ -1507,8 +1551,8 @@ impl OwnerV1 {
///
/// Identity доступна до первого update, но не заменяет полномочия этой
/// конкретной owner-эпохи.
pub(crate) fn content_identity(&self) -> ContentIdentityV3 {
ContentIdentityV3::from_core(self.compiled.content_identity())
pub(crate) fn content_identity(&self) -> ContentIdentityV4 {
ContentIdentityV4::from_core(self.compiled.content_identity())
}

/// Вычисляет верхние границы клеток для prospective Observed-update.
Expand Down Expand Up @@ -1828,10 +1872,10 @@ impl<'session> PreparedSessionTransitionV1<'session> {
/// Identity не идентифицирует owner-эпоху и не даёт runtime-полномочий.
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct ContentIdentityV3([u8; 32]);
pub(crate) struct ContentIdentityV4([u8; 32]);

impl ContentIdentityV3 {
const fn from_core(value: ProgramContentIdentityV3) -> Self {
impl ContentIdentityV4 {
const fn from_core(value: ProgramContentIdentityV4) -> Self {
Self(*value.as_bytes())
}

Expand All @@ -1849,8 +1893,8 @@ pub(crate) struct VerifiedCertificateV1<'a> {

impl<'a> VerifiedCertificateV1<'a> {
/// Возвращает identity скомпилированного содержания.
pub(crate) const fn content_identity(self) -> ContentIdentityV3 {
ContentIdentityV3::from_core(self.inner.report().content_identity())
pub(crate) const fn content_identity(self) -> ContentIdentityV4 {
ContentIdentityV4::from_core(self.inner.report().content_identity())
}

/// Возвращает точное наблюдение, на котором выдан сертификат.
Expand Down Expand Up @@ -1895,8 +1939,8 @@ pub(crate) struct ConflictCertificateV1<'a> {

impl<'a> ConflictCertificateV1<'a> {
/// Возвращает identity скомпилированного содержания.
pub(crate) const fn content_identity(self) -> ContentIdentityV3 {
ContentIdentityV3::from_core(self.inner.report().content_identity())
pub(crate) const fn content_identity(self) -> ContentIdentityV4 {
ContentIdentityV4::from_core(self.inner.report().content_identity())
}

/// Возвращает точное наблюдение, вызвавшее конфликт.
Expand Down Expand Up @@ -1945,7 +1989,7 @@ impl<'a> CertificateV1<'a> {
}

/// Возвращает identity скомпилированного содержания.
pub(crate) const fn content_identity(self) -> ContentIdentityV3 {
pub(crate) const fn content_identity(self) -> ContentIdentityV4 {
match self {
Self::Verified(value) => value.content_identity(),
Self::Conflict(value) => value.content_identity(),
Expand Down Expand Up @@ -3119,16 +3163,16 @@ fn map_program_compile_error(error: ProgramCompileError) -> CompileErrorV1 {
candidate: TargetCandidateIdV1::from_core(candidate),
}
}
ProgramCompileError::DuplicateTargetCandidateSignal {
ProgramCompileError::DuplicateTargetCandidateValue {
target,
first,
duplicate,
signal,
} => CompileErrorV1::DuplicateTargetCandidateSignal {
value,
} => CompileErrorV1::DuplicateTargetCandidateValue {
target: TargetIdV1::from_core(target),
first: TargetCandidateIdV1::from_core(first),
duplicate: TargetCandidateIdV1::from_core(duplicate),
encoded_srgb8: signal.srgb8(),
value: PaintValueV1(value),
},
ProgramCompileError::UnconstrainedTarget { target } => {
CompileErrorV1::UnconstrainedTarget {
Expand Down
59 changes: 55 additions & 4 deletions crates/labcolors-core/src/program/attachment/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use super::*;
use crate::Srgb8;
use crate::program::{
AppearanceContextV1, ConstraintIdV1, DraftV1, JointChoiceV1, JointStateV1, PaintIdV1,
ScenarioV1, SourceIdV1, StateKindV1, SurfaceIdV1, SurfaceInputPortIdV1, SurroundV1,
TargetCandidateIdV1, TargetCandidateV1, TargetIdV1,
PaintValueV1, ScenarioV1, SourceIdV1, StateKindV1, SurfaceIdV1, SurfaceInputPortIdV1,
SurroundV1, TargetCandidateIdV1, TargetCandidateV1, TargetIdV1,
};
use crate::wcag22::Wcag22CriterionV1;
use proptest::prelude::*;
Expand Down Expand Up @@ -1004,8 +1004,8 @@ fn allocator_owner() -> OwnerV1 {
TARGET,
SOURCE,
vec![
TargetCandidateV1::new(BLACK, Srgb8::new([0; 3])),
TargetCandidateV1::new(GRAY, Srgb8::new([0x80; 3])),
TargetCandidateV1::new(BLACK, PaintValueV1::opaque(Srgb8::new([0; 3]))),
TargetCandidateV1::new(GRAY, PaintValueV1::opaque(Srgb8::new([0x80; 3]))),
],
);
draft
Expand Down Expand Up @@ -1324,6 +1324,57 @@ fn verified_snapshot_mints_attached_render_output_and_exact_confirm_only_for_ide
assert_eq!(probe.revision(), Some(2));
}

#[test]
fn selected_nonopaque_finite_paint_reaches_sink_and_render_authority_atomically() {
const HALF_WHITE: TargetCandidateIdV1 = TargetCandidateIdV1::new(101);

let context = AppearanceContextV1::try_new(64.0, 0.2, SurroundV1::Average).unwrap();
let mut draft = DraftV1::new();
draft.push_source(SOURCE, Srgb8::new([0; 3]));
draft.push_finite_target(
TARGET,
SOURCE,
vec![TargetCandidateV1::new(
HALF_WHITE,
PaintValueV1::try_new(Srgb8::new([0xFF; 3]), 0.5).unwrap(),
)],
);
draft
.set_joint_selection(vec![JointStateV1::new(vec![JointChoiceV1::new(
TARGET, HALF_WHITE,
)])])
.unwrap();
draft.push_surface_input_port(INPUT);
draft.push_solid_paint(PAINT, TARGET);
draft.push_input_surface(INPUT_SURFACE, INPUT);
draft.push_source_over_occurrence(INNER, PAINT, INPUT_SURFACE, context);
draft.push_point_presentation_root(ROOT, INNER);
draft.push_point_presentation_target(ROOT, INNER);
draft.push_exact_hard(ConstraintIdV1::new(10), INNER, Srgb8::new([0x80; 3]));
draft.push_output(OUTPUT_A, PAINT);

let owner = draft.compile().unwrap();
let (sink, probe) = in_memory_point_sink(&[900]);
let emissions = [authored_emission(OUTPUT_A.value(), 900)];
let presentations = [authored_presentation(
OUTPUT_A.value(),
ROOT.value(),
INNER.value(),
)];
let mut attachment = owner.attach(71, &emissions, &presentations, sink).unwrap();
let black = [Srgb8::new([0; 3])];
let scenarios = [ScenarioV1::new(44, &black)];

let committed = attachment.update(observed(1, &scenarios)).unwrap();
let render = committed.render_outputs().next().unwrap().paint();
let snapshot = probe.snapshot();

assert_eq!(snapshot.len(), 1);
assert_eq!(snapshot[0].paint(), render);
assert_eq!(render.source(), Srgb8::new([0xFF; 3]));
assert_eq!(render.opacity_bits(), 0.5_f64.to_bits());
}

#[test]
fn one_emission_fans_out_to_every_distinct_attached_presentation() {
let owner = owner(
Expand Down
Loading
Loading