mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-10-18 07:33:46 +00:00
fix(ble): treat offset-0 write on stale buffer as restart
Per review: the central role writes with .withoutResponse, so legit inbound writes always land at offset 0. With a stale partial frame pending, the previous validation rejected the fresh write as invalid and dropped its bytes; clear the buffer and apply instead. Non-monotonic test now uses [8, 4] since offset 0 restarts; adds a test covering the stale-buffer restart path.
This commit is contained in:
parent
fd131a96a7
commit
b74e92958d
@ -41,6 +41,14 @@ struct BLEInboundWriteBuffer {
|
||||
for chunk in chunks where !chunk.data.isEmpty {
|
||||
offsets.append(chunk.offset)
|
||||
|
||||
// `.withoutResponse` writes always land at offset 0, so an
|
||||
// offset-0 chunk arriving on a non-empty buffer is a fresh frame
|
||||
// replacing stale partial bytes, not an overlap.
|
||||
if chunk.offset == 0, !combined.isEmpty {
|
||||
combined.removeAll()
|
||||
lastEnd = 0
|
||||
}
|
||||
|
||||
// Reject malformed writes before touching the buffer: a negative
|
||||
// offset traps `Data.replaceSubrange`, and non-monotonic or
|
||||
// overlapping offsets corrupt previously written bytes.
|
||||
|
||||
@ -190,19 +190,49 @@ struct BLEInboundWriteBufferTests {
|
||||
let result = buffer.append(
|
||||
chunks: [
|
||||
BLEInboundWriteChunk(offset: 8, data: Data(repeating: 0x01, count: 4)),
|
||||
BLEInboundWriteChunk(offset: 0, data: Data(repeating: 0x02, count: 4))
|
||||
BLEInboundWriteChunk(offset: 4, data: Data(repeating: 0x02, count: 4))
|
||||
],
|
||||
for: "central-1",
|
||||
capBytes: 1024
|
||||
)
|
||||
|
||||
if case let .invalid(metadata) = result {
|
||||
#expect(metadata.offsets == [8, 0])
|
||||
#expect(metadata.offsets == [8, 4])
|
||||
} else {
|
||||
Issue.record("Expected non-monotonic chunks to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func appendTreatsOffsetZeroOnStaleBufferAsRestart() throws {
|
||||
var buffer = BLEInboundWriteBuffer()
|
||||
let packet = makePacket(timestamp: 0x456)
|
||||
let frame = try #require(packet.toBinaryData(padding: false))
|
||||
let splitIndex = max(1, frame.count / 2)
|
||||
|
||||
// Stale partial frame left pending from an earlier write.
|
||||
_ = buffer.append(
|
||||
chunks: [BLEInboundWriteChunk(offset: 0, data: frame.prefix(splitIndex))],
|
||||
for: "central-1",
|
||||
capBytes: 1024
|
||||
)
|
||||
|
||||
// A fresh `.withoutResponse` write lands at offset 0 and replaces it
|
||||
// instead of being rejected as an overlap with the stale bytes.
|
||||
let result = buffer.append(
|
||||
chunks: [BLEInboundWriteChunk(offset: 0, data: frame)],
|
||||
for: "central-1",
|
||||
capBytes: 1024
|
||||
)
|
||||
|
||||
if case let .decoded(decoded, metadata) = result {
|
||||
#expect(decoded.timestamp == packet.timestamp)
|
||||
#expect(metadata.offsets == [0])
|
||||
} else {
|
||||
Issue.record("Expected offset-0 write to restart the stale buffer")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
func appendRejectsOverlapWithPreviouslyBufferedBytes() {
|
||||
var buffer = BLEInboundWriteBuffer()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user