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 <optional>
20 : : #include <string>
21 : : #include <string_view>
22 : : #include <vector>
23 : :
24 : : #include "wirestead/base/visibility.hpp"
25 : : #include "wirestead/framer/iframer.hpp"
26 : :
27 : : namespace wirestead {
28 : : namespace wrapper::detail {
29 : : struct ReceiveFramerAccess;
30 : : }
31 : : namespace framer {
32 : :
33 : : /**
34 : : * @brief Framer for text-based protocols (e.g., ASCII, NMEA).
35 : : *
36 : : * Buffers incoming data and extracts messages delimited by a specific sequence (e.g., "\n").
37 : : */
38 : : class WIRESTEAD_API LineFramer : public IFramer {
39 : : public:
40 : : /**
41 : : * @brief Construct a new Line Framer
42 : : *
43 : : * @param delimiter The delimiter string (default: "\n")
44 : : * @param include_delimiter Whether to include the delimiter in the extracted message (default: false)
45 : : * @param max_length Maximum message length before forcing a reset (default: 65536)
46 : : */
47 : : explicit LineFramer(std::string_view delimiter = "\n", bool include_delimiter = false, size_t max_length = 65536);
48 : :
49 : 2829 : ~LineFramer() override = default;
50 : :
51 : : void push_bytes(memory::ConstByteSpan data) override;
52 : : void on_message(MessageCallback cb) override;
53 : : void reset() override;
54 : :
55 : : private:
56 : : friend struct wrapper::detail::ReceiveFramerAccess;
57 : : std::string delimiter_;
58 : : bool include_delimiter_;
59 : : size_t max_length_;
60 : :
61 : : size_t scanned_idx_ = 0;
62 : : std::vector<uint8_t> buffer_;
63 : : MessageCallback on_message_;
64 : :
65 : : // Set when an in-progress message exceeded max_length_ and buffer_ was
66 : : // discarded before a delimiter was found. While true, push_bytes_internal
67 : : // discards incoming bytes (instead of buffering them) until it finds a
68 : : // delimiter, so the untransmitted tail of the discarded message can't be
69 : : // mistaken for the start of a fresh, valid message.
70 : : bool discarding_ = false;
71 : :
72 : : /**
73 : : * @brief Helper to scan data for delimiters and process messages.
74 : : *
75 : : * @param data The data to scan.
76 : : * @param search_start_offset The offset in data to start searching from.
77 : : * @return The number of bytes processed (emitted as messages).
78 : : */
79 : : size_t scan_and_process(memory::ConstByteSpan data, size_t search_start_offset);
80 : :
81 : : /**
82 : : * @brief Find the end of the next delimiter in data, if any.
83 : : *
84 : : * @param data The data to scan (searched from the beginning).
85 : : * @return The number of bytes up to and including the delimiter, or
86 : : * std::nullopt if no delimiter was found in data.
87 : : */
88 : : std::optional<size_t> skip_until_delimiter(memory::ConstByteSpan data) const;
89 : :
90 : : /**
91 : : * @brief Internal helper to process a manageable chunk of data.
92 : : */
93 : : void push_bytes_internal(memory::ConstByteSpan data);
94 : : };
95 : :
96 : : } // namespace framer
97 : : } // namespace wirestead
|