fix(streaming): keep the sign when counting samples until the buffer end - #510
Open
nicolas-rabault wants to merge 1 commit into
Open
fix(streaming): keep the sign when counting samples until the buffer end#510nicolas-rabault wants to merge 1 commit into
nicolas-rabault wants to merge 1 commit into
Conversation
Streaming_GetAvailableSampleNBUntilEndBuffer divided the pointer difference
before narrowing it to int32_t:
int32_t nb = ((uintptr_t)data_ptr - (uintptr_t)sample_ptr) / data_size;
That subtraction is unsigned, so a looped buffer gives a huge positive value
instead of a negative one and the "buffer have looped" branch is never taken.
Streaming_GetAvailableSampleNB, right above, already casts first. Match it.
The function then returns a number in the billions rather than the samples
left before the end of the ring. Its callers are the pipe services, which
size their reads with it.
Not caught until now because every existing channel uses a 1 byte data size,
where dividing by 1 leaves the bit pattern alone and the sign survives. A 3
byte data size shows it on any word size: the new test case gets 1431655764
where 1 is expected.
Also in this commit, two things that cannot change behaviour:
- Streaming_RmvAvailableSampleNB tested sample_ptr against end_ring_buffer in
the branch that is only entered when the new pointer stays below it, so the
test could never be true. The branch above already loops on the exact end.
- Streaming_GetSample assigned the result of memcpy back onto its own data
parameter, which goes nowhere.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Third and last of the streaming sweep. One real bug plus two things that cannot change behaviour.
1. Sign lost when counting samples until the buffer end
streaming.c:154 divided the pointer difference before narrowing it to
int32_t:(uintptr_t)a - (uintptr_t)bis unsigned arithmetic. On a looped buffer the difference is a near-UINTPTR_MAXvalue, and dividing that bydata_sizeyields a large positive number — so the loop branch is never taken and the function returns garbage instead of the samples left before the end of the ring.Streaming_GetAvailableSampleNBimmediately above already casts first (line 132). This just makes the two consistent.Why it was invisible
Every channel in the tree uses
data_size = 1—pipe.ccreates both of its channels that way, and so did every test. Dividing by 1 leaves the bit pattern untouched, so the sign survives and the bug cannot show.It is not a 32-bit-only problem either, which is what I first assumed. With a
data_sizethat is not a power of two the sign is lost on any word size:So the added test case uses a 10-sample, 3-byte channel and runs on the native 64-bit suite. Verified: it reports
Expected 1 Was 1431655764against the unfixed code.Blast radius
The callers are the pipe services (
SERIAL,WS/ESP32_IDF,WS/native,WS/ARDUINO), which use the result to size their reads. All aredata_size = 1, so no shipped configuration changes behaviour. This closes a trap for the firstdata_size > 1channel that calls it rather than fixing a live failure.2. Unreachable branch in
Streaming_RmvAvailableSampleNBThe
elseis entered only whensample_ptr + size * data_size < end_ring_buffer, so the new pointer is always strictly below the end. The branch above already handles landing exactly on it (chunk1 == size * data_size→chunk2 == 0→sample_ptr = ring_buffer), which the existing "buffer loop case" test covers. Removed.3. Dead assignment in
Streaming_GetSampledata = memcpy(data, stream->sample_ptr, ...)assigned the return value onto the function's owndataparameter. Removed the assignment, kept thememcpy.Tests
The new case fails on the unfixed code. No existing test modified.
clang-formatclean.Deliberately left alone
Two things from the same review that are not no-risk, so they are not here:
Streaming_GetSamplereturns0both for "read everything, buffer now empty" and for "nothing available". Callers cannot tell them apart. Fixing it means changing a public return contract;Luos_SendStreamingSizeignores the value so nothing is broken today.Streaming_AddAvailableSampleNBrefuses to fill a channel exactly (> 0) whileStreaming_PutSampleallows it (<= capacity). They disagree by one. An existing test pinsPutSample's behaviour, so picking a side is an API decision, not a cleanup.Also noted and not touched: a channel left zero-initialised and fed a message before
Streaming_CreateChanneldivides bydata_size == 0. Adding an assert there would be idiomatic but could newly fire in user code.