mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-25 08:27:39 +02:00
261 lines
5.7 KiB
Rust
261 lines
5.7 KiB
Rust
// (c) 2020-2022 ZeroTier, Inc. -- currently propritery pending actual release and licensing. See LICENSE.md.
|
|
|
|
use std::borrow::Cow;
|
|
|
|
/// A flat key/value store implemented in terms of arrays tuples of (key, value).
|
|
#[repr(transparent)]
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub struct FlatKV(Vec<(&'static str, Value)>);
|
|
|
|
/// Value variant for FlatKV.
|
|
#[derive(Clone, PartialEq, Eq)]
|
|
pub enum Value {
|
|
N,
|
|
KV(FlatKV),
|
|
S(Cow<'static, str>),
|
|
I(i64),
|
|
UI(u64),
|
|
B(bool),
|
|
Endpoint(crate::vl1::Endpoint),
|
|
Identity(crate::vl1::Identity),
|
|
}
|
|
|
|
impl Into<Value> for FlatKV {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::KV(self)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for &'static str {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::S(Cow::Borrowed(self))
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for String {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::S(Cow::Owned(self))
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for i64 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::I(self)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for u64 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::UI(self)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for isize {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::I(self as i64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for usize {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::UI(self as u64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for i32 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::I(self as i64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for u32 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::UI(self as u64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for i16 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::I(self as i64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for u16 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::UI(self as u64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for i8 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::I(self as i64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for u8 {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::UI(self as u64)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for bool {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::B(self)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for crate::vl1::Endpoint {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::Endpoint(self)
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for crate::vl1::InetAddress {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::Endpoint(crate::vl1::Endpoint::IpUdp(self))
|
|
}
|
|
}
|
|
|
|
impl Into<Value> for crate::vl1::Identity {
|
|
#[inline(always)]
|
|
fn into(self) -> Value {
|
|
Value::Identity(self)
|
|
}
|
|
}
|
|
|
|
impl ToString for Value {
|
|
fn to_string(&self) -> String {
|
|
match self {
|
|
Value::N => "(null)".into(),
|
|
Value::KV(x) => x.to_string(),
|
|
Value::S(x) => x.to_string(),
|
|
Value::I(x) => x.to_string(),
|
|
Value::UI(x) => x.to_string(),
|
|
Value::B(x) => x.to_string(),
|
|
Value::Endpoint(x) => x.to_string(),
|
|
Value::Identity(x) => x.to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FlatKV {
|
|
#[inline(always)]
|
|
pub fn add<T: Into<Value>>(&mut self, k: &'static str, v: T) {
|
|
self.0.push((k, v.into()))
|
|
}
|
|
}
|
|
|
|
fn json_escape(src: &str, escaped: &mut String) {
|
|
use std::fmt::Write;
|
|
let mut utf16_buf = [0u16; 2];
|
|
for c in src.chars() {
|
|
match c {
|
|
'\x08' => escaped.push_str("\\b"),
|
|
'\x0c' => escaped.push_str("\\f"),
|
|
'\n' => escaped.push_str("\\n"),
|
|
'\r' => escaped.push_str("\\r"),
|
|
'\t' => escaped.push_str("\\t"),
|
|
'"' => escaped.push_str("\\\""),
|
|
'\\' => escaped.push_str("\\\\"),
|
|
'/' => escaped.push_str("\\/"),
|
|
c if c.is_ascii_graphic() => escaped.push(c),
|
|
c => {
|
|
let encoded = c.encode_utf16(&mut utf16_buf);
|
|
for utf16 in encoded {
|
|
write!(escaped, "\\u{:04X}", utf16).unwrap();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for FlatKV {
|
|
#[inline(always)]
|
|
fn default() -> Self {
|
|
Self(Vec::new())
|
|
}
|
|
}
|
|
|
|
impl FlatKV {
|
|
#[inline(always)]
|
|
pub fn new() -> Self {
|
|
Self(Vec::new())
|
|
}
|
|
}
|
|
|
|
impl ToString for FlatKV {
|
|
/// Output a JSON formatted map of values or maps.
|
|
fn to_string(&self) -> String {
|
|
let mut first = true;
|
|
let mut tmp = String::new();
|
|
tmp.push_str("{ "); //} //"
|
|
for (k, v) in self.0.iter() {
|
|
if first {
|
|
first = false;
|
|
} else {
|
|
tmp.push_str(", ");
|
|
}
|
|
tmp.push('"');
|
|
json_escape(*k, &mut tmp);
|
|
tmp.push_str("\": ");
|
|
match v {
|
|
Value::S(_) | Value::Endpoint(_) | Value::Identity(_) => {
|
|
tmp.push('"');
|
|
json_escape(v.to_string().as_str(), &mut tmp);
|
|
tmp.push('"');
|
|
}
|
|
_ => tmp.push_str(v.to_string().as_str()),
|
|
}
|
|
}
|
|
tmp.push_str("} ");
|
|
tmp
|
|
}
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! kv {
|
|
($($key:expr => $value:expr,)+) => (kv!($($key => $value),+));
|
|
( $($key:expr => $value:expr),* ) => {
|
|
{
|
|
#[allow(unused_mut)]
|
|
let mut _kv = crate::util::flatkv::FlatKV(Vec::new());
|
|
$(
|
|
_kv.add($key, $value);
|
|
)*
|
|
_kv
|
|
}
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn kv_macro() {
|
|
let kv = kv!(
|
|
"foo" => 0_u64,
|
|
"bar" => "bar",
|
|
"baz" => -1_i64,
|
|
"lala" => false,
|
|
"lol" => kv!(
|
|
"boo" => 1_u16,
|
|
"far" => 2_u32,
|
|
)
|
|
);
|
|
}
|
|
}
|