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
286 changes: 286 additions & 0 deletions .fusa-reqs.json
Original file line number Diff line number Diff line change
Expand Up @@ -4087,6 +4087,292 @@
"status": "implemented",
"tc18": "§12.9.6 Table 30, TC18.txt L3804-3844",
"tc18_master_id": "TC18-12.9.6-004"
},
{
"id": "REQ-FRAG-001",
"title": "fragment_category() gives every FragmentErrc value a distinct, non-empty message",
"text": "rcp::fragment::fragment_category().message() shall return a non-empty string for every rcp::fragment::FragmentErrc value, including unrecognized ones, with each defined error code mapped to a distinct message.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-002",
"title": "plan_count() always plans a payload that already fits in one fragment (including an empty payload) to exactly one segment",
"text": "rcp::fragment::plan_count() shall return 1 for payload_len == 0 regardless of max_fragment_payload, and shall return 1 whenever payload_len <= max_fragment_payload.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.7.9 (\"In case an AVTPDU containing a single ACF_type would exceed the 'Max_AVTPDUsize' fragmentation as supported in ACF_ABB and ACF_GBB by the ms-bit will be performed.\"), TC18.txt L3408-3409",
"tc18_master_id": "TC18-12.7.9-003"
},
{
"id": "REQ-FRAG-003",
"title": "plan_count() returns 0 when fragmentation is disabled or the split exceeds the representable segment count",
"text": "rcp::fragment::plan_count() shall return 0 when max_fragment_payload == 0 and payload_len exceeds it, and shall return 0 when the resulting split would need more than kMaxIntermediateSegments intermediate segments to represent.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-004",
"title": "plan() plans a single ms=false segment for a payload that fits in one fragment",
"text": "rcp::fragment::plan() shall, when plan_count() reports 1, write exactly one Segment spanning the whole payload with ms == false.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented",
"tc18": "§11.2.1 Table 4 (\"ms 0b – the fragment sequence is completed with this ABB message\"), TC18.txt L1233; §12.7.9 (Max_AVTPDUsize fragmentation trigger), TC18.txt L3408-3409",
"tc18_master_id": "TC18-11.2.1-005"
},
{
"id": "REQ-FRAG-005",
"title": "plan() contiguously covers a multi-segment payload with correctly numbered intermediate segments",
"text": "rcp::fragment::plan() shall, when plan_count() reports more than 1, produce segments whose [offset, offset+len) ranges contiguously cover the whole payload, with every segment but the last carrying ms == true and a segment_num equal to its zero-based index, and the last segment carrying ms == false.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented",
"tc18": "§11.2.1 Table 4 (\"ms 0b – the fragment sequence is completed with this ABB message / 1b - more data to follow in one or more subsequent ABB messages\"; \"read_size/segment_num if op = 0 this is read_size, else segment_num\"), TC18.txt L1233-1235",
"tc18_master_id": "TC18-11.2.1-005"
},
{
"id": "REQ-FRAG-006",
"title": "plan() returns a distinct error code for each of its own precondition failures",
"text": "rcp::fragment::plan() shall return ErrDisabled or ErrTooManySegments under the same conditions plan_count() returns 0 for, and shall return ErrBadSegmentCount when segment_count does not match plan_count()'s answer for the same payload_len/max_fragment_payload.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-007",
"title": "to_string(ReasmResult) never returns null and gives every result value a distinct message",
"text": "rcp::fragment::to_string() shall return a non-null string for every rcp::fragment::ReasmResult value, including unrecognized ones, with each defined value mapped to a distinct message.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-008",
"title": "A freshly-constructed Reassembler starts empty and not collecting",
"text": "rcp::fragment::Reassembler's constructor shall leave the reassembler not collecting, with size() == 0 and data() == nullptr, and max_total_len set to the caller-supplied value.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-009",
"title": "Reassembler::reset() discards in-progress state while preserving max_total_len",
"text": "rcp::fragment::Reassembler::reset() shall discard any in-progress reassembly and any previously reassembled payload, leaving the reassembler in the same state a freshly-constructed Reassembler with the same max_total_len would be in.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-010",
"title": "A Reassembler's destruction releases its internal storage without leaking",
"text": "rcp::fragment::Reassembler shall release its internal storage when destroyed without leaking, whether or not a fragment sequence was in progress at the time -- satisfied here by RAII over its own fixed-capacity std::array member (this class holds no heap-allocated buffer of its own to explicitly free; see fragment.hpp's own \"Fixed-capacity from day one\" comment), unlike c-RCP's rcp_fragment_reassembler_destroy(), which frees a realloc()-grown heap buffer.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-011",
"title": "Reassembler::feed() completes immediately for a never-fragmented (ms=false) first fragment",
"text": "rcp::fragment::Reassembler::feed() shall, when fed a single ms=false fragment on a freshly-constructed-or-reset Reassembler, return ReasmResult::kComplete with that fragment's payload as the reassembled result, without entering the collecting state.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented",
"tc18": "§11.2.1 Table 4 (\"ms 0b – the fragment sequence is completed with this ABB message\"), TC18.txt L1233",
"tc18_master_id": "TC18-11.2.1-005"
},
{
"id": "REQ-FRAG-012",
"title": "Reassembler::feed() requires the first ms=true fragment of a sequence to carry segment_num 0",
"text": "rcp::fragment::Reassembler::feed() shall return ReasmResult::kErrOutOfOrder, without appending the fragment, when the first ms=true fragment fed to a freshly-constructed-or-reset Reassembler carries a segment_num other than 0.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-013",
"title": "Reassembler::feed() requires each subsequent ms=true fragment's segment_num to be exactly one more than the previous",
"text": "rcp::fragment::Reassembler::feed() shall return ReasmResult::kErrOutOfOrder, without appending the fragment and without disturbing already-collected state, when an ms=true fragment's segment_num is not exactly one more than the previously accepted fragment's segment_num.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-014",
"title": "Reassembler::feed() completes a sequence on any ms=false fragment regardless of its own segment_num field value",
"text": "rcp::fragment::Reassembler::feed() shall return ReasmResult::kComplete and append the fragment's payload when fed an ms=false fragment while collecting, without validating or consulting that fragment's segment_num argument.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented",
"tc18": "§11.2.1 Table 4 (\"ms 0b – the fragment sequence is completed with this ABB message\"), TC18.txt L1233",
"tc18_master_id": "TC18-11.2.1-005"
},
{
"id": "REQ-FRAG-015",
"title": "Reassembler::feed() rejects a fragment that would exceed max_total_len without mutating state",
"text": "rcp::fragment::Reassembler::feed() shall return ReasmResult::kErrTooLarge, leaving the reassembler's already-collected state unchanged, when appending the fed fragment's payload would cause the reassembled total to exceed max_total_len (further capped, always, by this Reassembler's own fixed kDefaultReassemblyCapacity).",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.7.7 Table 24 (\"rx_stream_max_request_size ... Max size of a request in bytes (longer requests will be rejected)\"), TC18.txt L3230-3232",
"tc18_master_id": "TC18-12.7.7-001"
},
{
"id": "REQ-FRAG-017",
"title": "Reassembler::is_collecting() reports whether a fragment sequence is currently in progress",
"text": "rcp::fragment::Reassembler::is_collecting() shall return true iff the reassembler has accepted at least one ms=true fragment since the last completed reassembly or reset, and false otherwise, without mutating the reassembler.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-FRAG-018",
"title": "Reassembler::data()/size() return the reassembled payload and its length after a completed reassembly",
"text": "rcp::fragment::Reassembler::data()/size(), called immediately after feed() has returned ReasmResult::kComplete, shall report a payload pointer and length equal to the concatenation, in segment_num order, of every fragment's payload accepted for that sequence.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented",
"tc18": "§11.2.1 Table 4 (\"ms\"/\"read_size/segment_num\" fields – the mechanism by which a split message's fragments are numbered so they can be reassembled), TC18.txt L1233-1235",
"tc18_master_id": "TC18-11.2.1-005"
},
{
"id": "REQ-LOAN-008",
"title": "BufferPool destruction frees every buffer still in the free list",
"text": "rcp::loan::BufferPool's destructor shall free every buffer currently in the pool's free list, then release the pool itself, without leaking.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-LOAN-009",
"title": "new_buffer_pool() allocates an empty, open pool",
"text": "rcp::loan::new_buffer_pool() shall return a non-null, open (ok()==true) rcp::loan::BufferPool with an empty free list (pooled_count() == 0).",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B",
"scope": "tc18",
"status": "implemented"
},
{
"id": "REQ-RMAP-059",
"title": "per-response/acknowledge-stream transmit queue enforces an octet-budgeted capacity",
"text": "rcp::respqueue::RespQueue shall provide an octet-budgeted FIFO of framed response/acknowledge messages (push()/push_seq()/pop()), with capacity enforced against a caller-supplied capacity_octets (regmap.hpp's ResponseQueueConfig::queue_size, quadlets already converted to octets by the caller), refusing a push that would exceed capacity_octets outright and leaving the queue unchanged when frame_len exceeds the entire configured capacity, unfixable by any amount of eviction. capacity_octets == 0 means unbounded.",
"standard": "iso26262",
"level": "QM",
"asil": "QM",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.7.9, TC18.txt L3403-L3404",
"tc18_master_id": "TC18-12.7.9-007"
},
{
"id": "REQ-RMAP-061",
"title": "Max_AVTPDUsize bounds every transmitted AVTPDU and is MTU-consistency-checkable",
"text": "rcp::respqueue::RespQueue::push()/push_seq() shall refuse (queue unchanged) any single frame whose own length exceeds the caller-supplied max_avtpdu_size_octets (regmap.hpp's ResponseQueueConfig::max_avtpdu_size, quadlets already converted to octets); RespQueue::max_avtpdu_size_within_mtu() shall report whether a candidate max_avtpdu_size_octets value fits within a caller-supplied mtu_budget_octets, for use before ever constructing a RespQueue.",
"standard": "iso26262",
"level": "QM",
"asil": "QM",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.7.9, TC18.txt L3405-L3407 (and Table 27 row 0x0002, L3427-3428)"
},
{
"id": "REQ-RMAP-062",
"title": "A single ACF message exceeding Max_AVTPDUsize is fragmented via the ms bit",
"text": "rcp::respqueue::RespQueue::max_fragment_payload() shall derive, from max_avtpdu_size_octets and a caller-supplied ACF header length, the max_fragment_payload budget rcp/fragment.hpp's plan_count()/plan() need to split a payload too large for one AVTPDU, conservatively reserving the fixed ACF header plus the worst-case 3 octets of trailing pad so the resulting budget is always safe.",
"standard": "iso26262",
"level": "QM",
"asil": "QM",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.7.9, TC18.txt L3408-L3409",
"tc18_master_id": "TC18-12.7.9-003"
},
{
"id": "REQ-RMAP-063",
"title": "flush_on_count triggers transmission of the whole queue packed into Max_AVTPDUsize-bounded AVTPDUs",
"text": "rcp::respqueue::RespQueue::should_flush() shall return true once the queue's own running octet total reaches or exceeds a caller-supplied flush_on_count_octets threshold; RespQueue::plan_batch() shall report how many of the queue's FIFO-ordered entries fit together within one max_avtpdu_size_octets-bounded AVTPDU, so a caller drains exactly that many via pop() and repeats until the queue is empty.",
"standard": "iso26262",
"level": "QM",
"asil": "QM",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.7.9, TC18.txt L3410-L3413 (and Table 27 row 0x0006, L3432-3436)",
"tc18_master_id": "TC18-12.7.9-004"
},
{
"id": "REQ-RMAP-064",
"title": "Flush_time forces transmission once the configured interval since the last transmission elapses",
"text": "rcp::respqueue::RespQueue::should_flush_by_time() shall return false when flush_time_us == 0 (\"flush only by count\"), and otherwise return true once a caller-tracked elapsed_since_last_transmit_us reaches flush_time_us, independently of flush_on_count.",
"standard": "iso26262",
"level": "QM",
"asil": "QM",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.7.9, TC18.txt L3414-L3415 (and Table 27 row 0x0008, L3437-3438)",
"tc18_master_id": "TC18-12.7.9-005"
},
{
"id": "REQ-RMAP-065",
"title": "an empty response queue still emits a heartbeat AVTPDU on Flush_time expiry",
"text": "PARTIAL: on Flush_time expiry the RC Server shall emit an AVTPDU from the queue even when that queue is empty, so the RC Client can observe that the server is alive. rcp::respqueue::RespQueue::should_flush_by_time() already fires identically for an empty queue, and RespQueue::plan_batch() already reports 0 for an empty queue -- so the empty heartbeat AVTPDU is fully constructible from existing primitives. What remains open: this is a protocol-primitives library, not a scheduler, and ships no emitter that actually drives this composition against a real clock and a real transport -- that composition is left to the integrator.",
"standard": "iso26262",
"level": "QM",
"asil": "QM",
"scope": "tc18-gap",
"status": "partial",
"tc18": "§12.7.9, TC18.txt L3416-L3417",
"tc18_master_id": "TC18-12.7.9-006"
},
{
"id": "REQ-RMAP-085",
"title": "TC18 §12.9.4/§12.9.5: a completely-full response/acknowledge queue evicts the lowest-sequence_num entry and sets the overflow bit",
"text": "rcp::respqueue::RespQueue::push()/push_seq() shall, once accepting an incoming frame would exceed capacity_octets or the queue's own fixed kMaxEntries slot bound, repeatedly evict the currently-queued entry with the numerically lowest sequence_num (not merely the FIFO-oldest) until enough capacity is freed, latching overflow() true on every eviction; a frame whose own length exceeds capacity_octets in its entirety shall be refused outright (queue unchanged, no eviction attempted).",
"standard": "iso26262",
"level": "QM",
"asil": "QM",
"scope": "tc18",
"status": "implemented",
"tc18": "§12.9.4/§12.9.5 (queue-full eviction and overflow-bit rule), see REQ-RMAP-059/061 for the byte-budget and per-message-ceiling siblings this rule is independent of"
}
]
}
15 changes: 14 additions & 1 deletion include/rcp/fragment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,22 @@
// fusa:req REQ-FRAG-013
// fusa:req REQ-FRAG-014
// fusa:req REQ-FRAG-015
// fusa:req REQ-FRAG-016
// fusa:req REQ-FRAG-017
// fusa:req REQ-FRAG-018
//
// REQ-FRAG-016 (rcp_fragment_reasm_result_t's own RCP_FRAGMENT_REASM_ERR_
// ALLOC, "reports an allocation failure distinctly from every other failure
// mode") deliberately has no catalog entry or tag here: it is structurally
// inapplicable, not missing behavior. Reassembler is backed by a fixed
// std::array member (see "Fixed-capacity from day one" below), not
// c-RCP's own realloc()-grown heap buffer, so there is no allocation on the
// reassembly path at all to ever fail -- any attempt to grow past the fixed
// capacity is already covered by ReasmResult::kErrTooLarge (REQ-FRAG-015).
// c-RCP's own REQ-FRAG-016 carries no TC18 citation of its own either, so
// this is a documented judgment call (matching this codebase's established
// convention -- see e.g. Phase 6 batch 1's REQ-ACF-037/REQ-AVTP-016/027 --
// for an architecture divergence with nothing TC18-scoped to file as a gap),
// not a gap filed to .fusa-reqs-pending.json.

// Multi-AVTPDU message fragmentation and reassembly for the TC18 Remote
// Control Protocol wire layer (TC18 §13.7.11.3) — brand new to cpp-RCP.
Expand Down
Loading
Loading