morph
0.1.0
Asynchronous bridge between GUI thread and business models
Toggle main menu visibility
Loading...
Searching...
No Matches
outbound_ledger.hpp
Go to the documentation of this file.
1
// SPDX-License-Identifier: Apache-2.0
2
3
#pragma once
4
#include <QtGlobal>
5
#include <chrono>
6
#include <cstddef>
7
#include <deque>
8
#include <optional>
9
10
/// @file
11
/// @brief Which frames handed to a socket it has written, told from the
12
/// socket's own byte counts.
13
14
namespace
morph::qt::detail {
15
16
/// @brief Frames handed to one socket and not yet written, oldest first, each
17
/// with the position in the socket's running count of written bytes at
18
/// which its last byte is out.
19
///
20
/// A frame's position is taken when it is handed over: the bytes written so
21
/// far, plus every byte the socket still holds, the frame's own included. So
22
/// it is measured, not computed from a frame layout, and covers whatever the
23
/// socket queued for itself ahead of it. Over TLS the bytes held must include
24
/// the ciphertext not yet sent as well as the plaintext not yet encrypted;
25
/// plaintext is held unencrypted only briefly, so a frame can be released
26
/// ahead of its last byte by at most the record overhead on what was then
27
/// unencrypted, and the error does not accumulate: each position is taken
28
/// afresh.
29
///
30
/// Not synchronised: owned by the socket's thread.
31
class
OutboundLedger {
32
public
:
33
/// @brief Records a frame just handed to the socket.
34
/// @param pending Bytes the socket holds after taking the frame,
35
/// the frame's own included.
36
/// @param counted What the frame added to the connection's backlog.
37
/// @param queuedAt When the frame was handed over.
38
void
push(qint64 pending, std::size_t counted, std::chrono::steady_clock::time_point queuedAt) {
39
_frames.push_back(Frame{.end = _written + pending, .counted = counted, .queuedAt = queuedAt});
40
}
41
42
/// @brief Advances the written count and releases every frame it completes.
43
/// @param bytes Bytes the socket just reported written.
44
/// @param pending Bytes the socket still holds after writing them. `0`
45
/// releases every frame, whatever its position.
46
/// @return The sum of the released frames' `counted`.
47
[[nodiscard]] std::size_t written(qint64 bytes, qint64 pending) {
48
_written += bytes;
49
std::size_t released = 0;
50
while
(!_frames.empty() && (pending == 0 || _frames.front().end <= _written)) {
51
released += _frames.front().counted;
52
_frames.pop_front();
53
}
54
return
released;
55
}
56
57
/// @brief Forgets every frame, as when the connection goes away.
58
/// @return The sum of the forgotten frames' `counted`.
59
[[nodiscard]] std::size_t clear() {
60
std::size_t dropped = 0;
61
for
(
auto
const
& frame : _frames) {
62
dropped += frame.counted;
63
}
64
_frames.clear();
65
return
dropped;
66
}
67
68
/// @brief When the oldest unwritten frame was handed over.
69
/// @return Its time, or `std::nullopt` when every frame is written.
70
[[nodiscard]] std::optional<std::chrono::steady_clock::time_point> oldest()
const
{
71
if
(_frames.empty()) {
72
return
std::nullopt;
73
}
74
return
_frames.front().queuedAt;
75
}
76
77
private
:
78
/// @brief One frame handed to the socket and not yet written.
79
struct
Frame {
80
/// @brief Written count at which the frame's last byte is out.
81
qint64 end = 0;
82
/// @brief What the frame added to the backlog: its envelope's size.
83
std::size_t counted = 0;
84
/// @brief When the frame was handed over.
85
std::chrono::steady_clock::time_point queuedAt;
86
};
87
88
std::deque<Frame> _frames;
89
qint64 _written = 0;
90
};
91
92
}
// namespace morph::qt::detail
morph
qt
detail
outbound_ledger.hpp
Generated by
1.17.0