Skip to content

Hold 0050's budget, its floor and the order a decode waiting for room is started in - #257

Merged
iderex merged 2 commits into
mainfrom
the-decoded-bytes-budget-and-its-admission-50
Aug 31, 2026
Merged

Hold 0050's budget, its floor and the order a decode waiting for room is started in#257
iderex merged 2 commits into
mainfrom
the-decoded-bytes-budget-and-its-admission-50

Conversation

@iderex

@iderex iderex commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

The issue this belongs to

Refs #50. It does not close it, and the reason is under "What this does not
cover" below.

What changed

src/artwork/budget.rs holds
0050's bound and the admission
rule that keeps it. Two hundred decoded tiles held at once is enough to be killed
by the operating system on a television, and that failure looks like a crash
rather than like a cache that was too large.

It is a working set and never a store, which is most of 0050's value and the
thing an implementation gets wrong first. 0009 hands a decoded image to the caller
and never reads it again, and 0006 caches encoded artwork bytes and the declared
dimensions rather than pixels, so what is counted is the buffers of decodes that
are running plus those whose outcome has not reached the caller, and nothing else.
Read the other way, the natural build is a second store with a second eviction
policy for data 0006 deliberately does not keep, and the memory it occupies is the
memory the budget exists to bound.

Nothing fails at the bound. A decode that would take the total past the budget
does not start; it waits. There is no sixteenth kind in 0004 for it and none is
asked for, so the answer type is WhatTheAskDoes and not a Result - a failure
here would be one whose occurrence depends on what other callers were doing, which
is the least reproducible thing a client can be handed.

The floor is derived rather than chosen, which is 0050's own construction:
0055's per-image bound at four bytes a pixel. DecodedBytes::of answers None
for dimensions 0055 refuses, and that is what makes the budget total rather than
nearly total - every buffer size this module can produce is no larger than the
smallest budget a client may set, so nothing can wait for room that will never
exist.

The order is where a queue written from the number alone goes wrong. A decode
that would fit at this instant still waits behind one that does not, because
ordering by size starves the large ones. The test asserts the poster fits before
asserting it waits, so it is a test of the order rather than of the room.

Two figures in 0050 that do not follow from its own arithmetic

Both are already recorded on #50 by an earlier reading, and neither is repaired
here: 0001 admits neither as an edit to a landed record. They are named at the top
of the module because this file is where an implementation written from those
sentences would go wrong, and this is the paragraph a reader of the module needs
rather than a second finding.

The one that costs something: 0050 says that on a four-processor television three
maximal images cannot be decoded at once and the THIRD waits. Two maximal images
are 128000000 bytes against a budget of 67108864, so it is the second, and one
maximal image fits with nothing beside it. Admission written to that sentence
would allow a hundred and twenty eight megabytes on the device the per-image bound
in 0055 was chosen against, which is the allocation that record exists to refuse
arriving by the route 0050 names in its own first paragraph. The module is written
to the arithmetic, and one_maximal_image_fits_and_the_second_waits is what would
redden if somebody wrote it to the sentence.

The other is that the record calls the budget and the per-image bound within four
per cent of each other; it is 4.63 per cent of the budget and 4.86 per cent of the
bound. It changes nothing here and is named for completeness.

Evidence

At 2c919ac, the two commands the gate runs:

cargo build --locked --all-targets
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.41s

cargo test --locked | grep 'test result'
test result: ok. 429 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 97 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Fourteen of those are this module's, and five of the 97 are 0009's thread
statements for its types, checked from outside the crate the way every other one
is.

The analyser, the formatter and the two shell legs that run on this machine:

cargo clippy --locked --all-targets -- -D clippy::all -D clippy::pedantic -D clippy::cargo ; echo "exit=$?"
exit=0

cargo fmt --check ; echo "exit=$?"
exit=0

bash .github/invariants/invariants.sh check | tail -1
Every rule above was applied to its subject and refused nothing.

bash .github/doc-paths/doc-paths.sh check | tail -1
Every path these documents name resolves against the tracked set.

The guards refusing

Three deliberate violations, each reddening what it names.

The order half of the admission removed, so a small decode can step past a
large one that is already waiting:

-        if self.waiting.is_empty() && self.fits(bytes) {
+        if self.fits(bytes) {

test artwork::budget::tests::a_later_small_ask_does_not_step_past_an_earlier_large_one ... FAILED
test result: FAILED. 409 passed; 1 failed

The floor lowered, so a client may set a budget under 0055's per-image bound
and make a legal image undecodable:

-        if bytes < THE_FLOOR_A_CLIENT_MAY_SET {
+        if bytes < 1 {

test artwork::budget::tests::a_budget_below_the_per_image_bound_is_refused_with_both_numbers ... FAILED
test result: FAILED. 409 passed; 1 failed

Three bytes a pixel instead of four, which is the arithmetic every number in
0050 rests on:

-            bytes: dimensions.pixel_count() * BYTES_A_PIXEL,
+            bytes: dimensions.pixel_count() * 3,

test result: FAILED. 405 passed; 5 failed

The tree is restored in each case, and the run above is the restored tree.

A fourth refusal was not deliberate and is worth reporting as such.
no-view-vocabulary refused four lines of the module's own prose on the first
local run of the invariants leg, because they said what a tile did on a screen
rather than what a client withdrew. The rule is right - 0003 places a scroll
offset outside the core by name - and the second commit on this branch is that
repair. It is named here rather than tidied away, because a guard catching this
change is exactly what the guard is for.

The means

Rust in the crate that already exists, in src/artwork/, beside the module that
holds the record this one derives its floor from. No dependency is added and no
new language enters the tree. The types are plain values and one piece of
bookkeeping, which is what lets 0009's thread statements be compile-time
assertions rather than something a run has to observe; DecodedBytesHeld carries
no interior mutability, so whoever holds it serialises access to it, the way the
cache bookkeeping is held.

What this does NOT cover

#50 is not closed by this. Its two conditions are a test proving the caller's
thread is not used for decode, and a test driving two hundred images through
against the bound. Nothing in this tree starts a thread and nothing turns admitted
bytes into pixels, so neither has a subject. The observation that the budget could
be built ahead of both was already recorded on #50 by an earlier reading; this is
that piece and it is not those conditions.

Nothing decodes. The sizes this module is told are the sizes a decoder would
allocate, and no buffer is allocated anywhere. A caller that reports a size other
than the one it allocated is not caught here and cannot be.

Where the lane comes from is #115. 0050 declines to add a second bound on how
many decodes run at once, because 0009 already sizes the processing lane, and this
module holds neither the lane nor a number for it.

Nothing reports a waiting decode. a_wait_worth_reporting answers whether the
five seconds 0050 names have passed; emitting the event through 0100 is the
diagnostics facility and is not done here, so no run of this tree reports one.

The two figures above are recorded, not repaired. 0050 still carries both
readings on the default branch, and an implementation written from the sentence
rather than from the arithmetic would still pass a reading of that record.

No second reader. Nobody other than the author has read this change.

iderex added 2 commits August 31, 2026 07:21
… is started in

Two hundred decoded tiles held at once is enough to be killed by the operating
system on a television, and that failure looks like a crash rather than like a
cache that was too large. 0050 bounds it at sixty four mebibytes, and this is the
admission rule that keeps the bound: a decode that would take the total past it
does not start, it waits, and nothing fails.

Most of the work in 0050 was deciding what the quantity is, because the phrase
reads as a cache and is not one, and that is what this module is written to.
0009 hands a decoded image to the caller and never reads it again and 0006 caches
encoded bytes rather than pixels, so what is counted is the buffers of decodes
that are running plus those whose outcome has not reached the caller, and nothing
else. Read the other way, the natural build is a store with an eviction policy
for data 0006 deliberately does not keep.

The floor is derived rather than chosen, which is 0050's own construction: it is
0055's per-image bound at four bytes a pixel, so no image the core will ever
decode is larger than the smallest budget a client may set, and nothing can wait
for room that will never exist. A buffer size exists only for dimensions 0055
admits, which is what makes that total rather than nearly total.

The order is where a queue written from the number alone goes wrong. A decode
that would fit right now still waits behind one that does not, because ordering
by size starves the large ones, and the test for it asserts the poster fits
before asserting it waits, so it is a test of the order rather than of the room.

Two figures 0050 states do not follow from its own arithmetic. Both are already
recorded on #50 and neither is repaired here, because 0001 admits neither as an
edit; both are named at the top of this module because this is where an
implementation written from those sentences would go wrong. The one that costs
something says the THIRD maximal image waits on a four-processor television. Two
maximal images are 128000000 bytes against a budget of 67108864, so it is the
second, and admission written to the sentence would allow a hundred and twenty
eight megabytes on the device the per-image bound was chosen against.

Fourteen tests, and three deliberate violations reddening what they should:

    if self.fits(bytes) {                   // the queue-is-empty half removed
    test artwork::budget::tests::a_later_small_ask_does_not_step_past_an_earlier_large_one ... FAILED
    test result: FAILED. 409 passed; 1 failed

    if bytes < 1 {                          // the floor lowered
    test artwork::budget::tests::a_budget_below_the_per_image_bound_is_refused_with_both_numbers ... FAILED
    test result: FAILED. 409 passed; 1 failed

    dimensions.pixel_count() * 3            // three bytes a pixel
    test result: FAILED. 405 passed; 5 failed

Refs #50. Its conditions ask for a caller's thread not being used and two hundred
images driven through, and nothing in this tree decodes anything or starts a
thread, so this closes nothing.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
`no-view-vocabulary` refused four lines of the module that just landed on this
branch, and the rule is right rather than pedantic: 0003 places a scroll offset
outside the core by name and says a core that learns any of those words has
learned the shape of one client's screen. The sentences meant a tile a client no
longer wants, which is 0053's withdrawal and a thing the core does know about, so
they now say that.

    ::error::no-view-vocabulary: src/artwork/budget.rs:104
    ::error::no-view-vocabulary: src/artwork/budget.rs:105
    ::error::no-view-vocabulary: src/artwork/budget.rs:336
    ::error::no-view-vocabulary: src/artwork/budget.rs:585
    ::error::4 line(s) broke an invariant above.

Refs #50.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit ab7a877 into main Aug 31, 2026
24 checks passed
@iderex
iderex deleted the the-decoded-bytes-budget-and-its-admission-50 branch August 31, 2026 05:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant