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 framer {
26 : :
27 : : /**
28 : : * @brief Framer for binary packet protocols delimited by byte patterns.
29 : : *
30 : : * Syncs by searching for the start pattern, then collects data until the end
31 : : * pattern is found.
32 : : *
33 : : * @warning **The payload must not be able to contain the end pattern.** There
34 : : * is no escaping or byte stuffing: the end pattern is located with a plain
35 : : * search over the collected bytes, so the first occurrence ends the frame
36 : : * wherever it appears. A binary protocol whose payload spans arbitrary byte
37 : : * values will therefore be cut short as soon as those bytes happen to match,
38 : : * and the remainder is resynchronised as if it were a new packet - silently,
39 : : * since a truncated frame is indistinguishable from a short one.
40 : : *
41 : : * That makes this framer a fit for protocols with a reserved delimiter (a
42 : : * text-ish or escaped wire format), and a poor fit for the common
43 : : * length-prefixed binary layout. Use LengthPrefixFramer for that - the length
44 : : * field says exactly how many bytes to collect, so no byte value is ever
45 : : * special:
46 : : *
47 : : * ```cpp
48 : : * auto ch = wirestead::tcp_client("127.0.0.1", 9000)
49 : : * .use_length_prefix_framer(4)
50 : : * .on_message(...)
51 : : * .build();
52 : : * ```
53 : : */
54 : : class WIRESTEAD_API PacketFramer : public IFramer {
55 : : public:
56 : : /**
57 : : * @brief Construct a new Packet Framer
58 : : *
59 : : * @param start_pattern The start pattern bytes
60 : : * @param end_pattern The end pattern bytes
61 : : * @param max_length Maximum packet length (including patterns) before reset
62 : : */
63 : : PacketFramer(const std::vector<uint8_t>& start_pattern, const std::vector<uint8_t>& end_pattern, size_t max_length);
64 : :
65 : 53 : ~PacketFramer() override = default;
66 : :
67 : : void push_bytes(memory::ConstByteSpan data) override;
68 : : void on_message(MessageCallback cb) override;
69 : : void reset() override;
70 : :
71 : : private:
72 : : enum class State {
73 : : Sync, // Waiting for start pattern
74 : : Collect // Collecting data until end pattern
75 : : };
76 : :
77 : : std::vector<uint8_t> start_pattern_;
78 : : std::vector<uint8_t> end_pattern_;
79 : : size_t max_length_;
80 : :
81 : : State state_;
82 : : std::vector<uint8_t> buffer_;
83 : : MessageCallback on_message_;
84 : :
85 : : // Optimization: Track where we stopped scanning for end pattern
86 : : // to avoid re-scanning the entire buffer on each push.
87 : : size_t scanned_idx_ = 0;
88 : : };
89 : :
90 : : } // namespace framer
91 : : } // namespace wirestead
|