memberlist_proto/
lib.rs

1//! Types used by the [`memberlist`](https://crates.io/crates/memberlist) crate.
2#![doc(html_logo_url = "https://raw.githubusercontent.com/al8n/memberlist/main/art/logo_72x72.png")]
3#![forbid(unsafe_code)]
4#![deny(warnings)]
5#![allow(clippy::type_complexity, clippy::double_parens, unexpected_cfgs)]
6#![cfg_attr(docsrs, feature(doc_cfg))]
7#![cfg_attr(docsrs, allow(unused_attributes))]
8
9#[cfg(feature = "metrics")]
10#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
11pub use metrics_label::MetricLabels;
12#[cfg(any(feature = "std", feature = "alloc"))]
13#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
14pub use nodecraft::{CheapClone, Node, NodeId, ParseNodeIdError};
15
16#[cfg(all(any(feature = "std", feature = "alloc"), feature = "hostaddr"))]
17#[cfg_attr(
18  docsrs,
19  doc(cfg(all(any(feature = "std", feature = "alloc"), feature = "hostaddr")))
20)]
21pub use nodecraft::{
22  Domain, DomainBuffer, HostAddr, HostAddrBuffer,
23  hostaddr::{ParseDomainError, ParseHostAddrError},
24};
25
26#[cfg(any(feature = "arbitrary", test))]
27mod arbitrary_impl;
28
29/// Compression related types.
30#[cfg(any(
31  feature = "zstd",
32  feature = "lz4",
33  feature = "brotli",
34  feature = "snappy",
35))]
36pub mod compression;
37
38/// Checksum related types.
39#[cfg(any(
40  feature = "crc32",
41  feature = "xxhash32",
42  feature = "xxhash64",
43  feature = "xxhash3",
44  feature = "murmur3",
45))]
46pub mod checksum;
47
48/// Encryption related types.
49#[cfg(feature = "encryption")]
50#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
51pub mod encryption;
52
53#[cfg(feature = "metrics")]
54mod metrics_label;
55#[cfg(any(feature = "quickcheck", test))]
56mod quickcheck_impl;
57#[cfg(feature = "serde")]
58mod serde_impl;
59
60#[cfg(any(
61  feature = "crc32",
62  feature = "xxhash32",
63  feature = "xxhash64",
64  feature = "xxhash3",
65  feature = "murmur3",
66))]
67pub use checksum::*;
68#[cfg(any(
69  feature = "zstd",
70  feature = "lz4",
71  feature = "brotli",
72  feature = "snappy",
73))]
74pub use compression::*;
75#[cfg(feature = "encryption")]
76#[cfg_attr(docsrs, doc(cfg(feature = "encryption")))]
77pub use encryption::*;
78
79pub use ack::*;
80pub use address::*;
81pub use alive::*;
82pub use bad_state::*;
83pub use bytes;
84pub use cidr_policy::*;
85pub use data::*;
86pub use err::*;
87pub use label::*;
88pub use meta::*;
89pub use payload::*;
90pub use ping::*;
91pub use proto::*;
92pub use push_pull::*;
93pub use server::*;
94pub use smallvec_wrapper::*;
95pub use version::*;
96
97mod ack;
98mod address;
99mod alive;
100mod bad_state;
101mod cidr_policy;
102mod data;
103mod err;
104mod label;
105mod meta;
106mod payload;
107mod ping;
108mod proto;
109mod push_pull;
110mod server;
111mod version;
112
113/// A wire type used in Protobuf-like encoding/decoding.
114#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
115#[repr(u8)]
116#[non_exhaustive]
117pub enum WireType {
118  /// A byte wire type.
119  Byte = 0,
120  /// A varint wire type.
121  Varint = 1,
122  /// A length-delimited wire type.
123  LengthDelimited = 2,
124  /// Fixed 32-bit wire type.
125  Fixed32 = 3,
126  /// Fixed 64-bit wire type.
127  Fixed64 = 4,
128}
129
130impl WireType {
131  /// Returns the [`WireType`] as a `&'static str`.
132  #[inline]
133  pub const fn as_str(&self) -> &'static str {
134    match self {
135      Self::Byte => "byte",
136      Self::Varint => "varint",
137      Self::LengthDelimited => "length-delimited",
138      Self::Fixed32 => "fixed32",
139      Self::Fixed64 => "fixed64",
140    }
141  }
142}
143
144impl core::fmt::Display for WireType {
145  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
146    write!(f, "{}", self.as_str())
147  }
148}
149
150impl TryFrom<u8> for WireType {
151  type Error = u8;
152
153  fn try_from(value: u8) -> Result<Self, Self::Error> {
154    Ok(match value {
155      0 => Self::Byte,
156      1 => Self::Varint,
157      2 => Self::LengthDelimited,
158      3 => Self::Fixed32,
159      4 => Self::Fixed64,
160      _ => return Err(value),
161    })
162  }
163}
164
165use utils::*;
166
167/// Utils for protobuf-like encoding/decoding
168pub mod utils {
169  use super::{DecodeError, WireType};
170
171  /// Merge wire type and tag into a byte.
172  #[inline]
173  pub const fn merge(ty: WireType, tag: u8) -> u8 {
174    (ty as u8) << 3 | tag
175  }
176
177  /// Split a byte into wire type and tag.
178  #[inline]
179  pub const fn split(val: u8) -> (u8, u8) {
180    let wire_type = val >> 3; // Shift right to get the wire type
181    let tag = val & 0b111; // Mask with 0b111 to get last 3 bits
182    (wire_type, tag)
183  }
184
185  /// Skip a field in the buffer.
186  pub fn skip(ty: &'static str, src: &[u8]) -> Result<usize, DecodeError> {
187    let buf_len = src.len();
188    if buf_len == 0 {
189      return Ok(0);
190    }
191
192    let mut offset = 0;
193    let (wire_type, tag) = split(src[offset]);
194
195    let wire_type =
196      WireType::try_from(wire_type).map_err(|v| DecodeError::unknown_wire_type(ty, v, tag))?;
197    offset += 1;
198    let src = &src[offset..];
199    match wire_type {
200      WireType::Varint => match varing::decode_u64_varint(src) {
201        Ok((bytes_read, _)) => Ok((offset + bytes_read.get()).min(buf_len)),
202        Err(e) => Err(e.into()),
203      },
204      WireType::LengthDelimited => {
205        // Skip length-delimited field by reading the length and skipping the payload
206        if src.is_empty() {
207          return Err(DecodeError::buffer_underflow());
208        }
209
210        match varing::decode_u32_varint(src) {
211          Ok((bytes_read, length)) => {
212            Ok((offset + bytes_read.get() + length as usize).min(buf_len))
213          }
214          Err(e) => Err(e.into()),
215        }
216      }
217      WireType::Byte => Ok((offset + 1).min(buf_len)),
218      WireType::Fixed32 => Ok((offset + 4).min(buf_len)),
219      WireType::Fixed64 => Ok((offset + 8).min(buf_len)),
220    }
221  }
222}
223
224#[cfg(debug_assertions)]
225#[inline]
226fn debug_assert_write_eq<T: ?Sized>(actual: usize, expected: usize) {
227  debug_assert_eq!(
228    actual,
229    expected,
230    "{}: expect writting {expected} bytes, but actual write {actual} bytes",
231    core::any::type_name::<T>()
232  );
233}
234
235#[cfg(debug_assertions)]
236#[inline]
237fn debug_assert_read_eq<T: ?Sized>(actual: usize, expected: usize) {
238  debug_assert_eq!(
239    actual,
240    expected,
241    "{}: expect reading {expected} bytes, but actual read {actual} bytes",
242    core::any::type_name::<T>()
243  );
244}
245
246#[inline]
247fn check_encoded_message_size(required: usize) -> Result<(), EncodeError> {
248  if required > u32::MAX as usize {
249    return Err(EncodeError::TooLarge);
250  }
251
252  Ok(())
253}