Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #pragma once
18 : :
19 : : #include <vector>
20 : :
21 : : #include "wirestead/base/visibility.hpp"
22 : : #include "wirestead/framer/iframer.hpp"
23 : :
24 : : namespace wirestead {
25 : : namespace wrapper::detail {
26 : : struct ReceiveFramerAccess;
27 : : }
28 : : namespace framer {
29 : :
30 : : /**
31 : : * @brief Framer for binary packet protocols delimited by byte patterns.
32 : : *
33 : : * Syncs by searching for the start pattern, then collects data until the end
34 : : * pattern is found.
35 : : *
36 : : * @warning **The payload must not be able to contain the end pattern.** There
37 : : * is no escaping or byte stuffing: the end pattern is located with a plain
38 : : * search over the collected bytes, so the first occurrence ends the frame
39 : : * wherever it appears. A binary protocol whose payload spans arbitrary byte
40 : : * values will therefore be cut short as soon as those bytes happen to match,
41 : : * and the remainder is resynchronised as if it were a new packet - silently,
42 : : * since a truncated frame is indistinguishable from a short one.
43 : : *
44 : : * That makes this framer a fit for protocols with a reserved delimiter (a
45 : : * text-ish or escaped wire format), and a poor fit for the common
46 : : * length-prefixed binary layout. Use LengthPrefixFramer for that - the length
47 : : * field says exactly how many bytes to collect, so no byte value is ever
48 : : * special:
49 : : *
50 : : * ```cpp
51 : : * auto ch = wirestead::tcp_client("127.0.0.1", 9000)
52 : : * .use_length_prefix_framer(4)
53 : : * .on_message(...)
54 : : * .build();
55 : : * ```
56 : : */
57 : : class WIRESTEAD_API PacketFramer : public IFramer {
58 : : public:
59 : : /**
60 : : * @brief Construct a new Packet Framer
61 : : *
62 : : * @param start_pattern The start pattern bytes
63 : : * @param end_pattern The end pattern bytes
64 : : * @param max_length Maximum packet length (including patterns) before reset
65 : : */
66 : : PacketFramer(const std::vector<uint8_t>& start_pattern, const std::vector<uint8_t>& end_pattern, size_t max_length);
67 : :
68 : 53 : ~PacketFramer() override = default;
69 : :
70 : : void push_bytes(memory::ConstByteSpan data) override;
71 : : void on_message(MessageCallback cb) override;
72 : : void reset() override;
73 : :
74 : : private:
75 : : friend struct wrapper::detail::ReceiveFramerAccess;
76 : : enum class State {
77 : : Sync, // Waiting for start pattern
78 : : Collect // Collecting data until end pattern
79 : : };
80 : :
81 : : std::vector<uint8_t> start_pattern_;
82 : : std::vector<uint8_t> end_pattern_;
83 : : size_t max_length_;
84 : :
85 : : State state_;
86 : : std::vector<uint8_t> buffer_;
87 : : MessageCallback on_message_;
88 : :
89 : : // Optimization: Track where we stopped scanning for end pattern
90 : : // to avoid re-scanning the entire buffer on each push.
91 : : size_t scanned_idx_ = 0;
92 : : };
93 : :
94 : : } // namespace framer
95 : : } // namespace wirestead
|