use std::{borrow::Cow, fmt};

use crate::stack::StackString;
use crate::KStringCowBase;
use crate::KStringRef;

pub(crate) type StdString = std::string::String;

/// A UTF-8 encoded, immutable string.
pub type KString = KStringBase<crate::backend::DefaultStr>;

/// A UTF-8 encoded, immutable string.
#[derive(Clone)]
#[repr(transparent)]
pub struct KStringBase<B> {
    inner: KStringInner<B>,
}

impl<B> KStringBase<B> {
    pub const EMPTY: Self = KStringBase::from_static("");

    /// Create a new empty `KStringBase`.
    #[inline]
    #[must_use]
    pub fn new() -> Self {
        Self::EMPTY
    }

    /// Create a reference to a `'static` data.
    #[inline]
    #[must_use]
    pub const fn from_static(other: &'static str) -> Self {
        Self {
            inner: KStringInner::from_static(other),
        }
    }

    /// Create an inline string, if possible
    #[inline]
    #[must_use]
    pub fn try_inline(other: &str) -> Option<Self> {
        KStringInner::try_inline(other).map(|inner| Self { inner })
    }
}

impl<B: crate::backend::HeapStr> KStringBase<B> {
    /// Create an owned `KStringBase`.
    #[inline]
    #[must_use]
    pub fn from_boxed(other: crate::backend::BoxedStr) -> Self {
        Self {
            inner: KStringInner::from_boxed(other),
        }
    }

    /// Create an owned `KStringBase`.
    #[inline]
    #[must_use]
    pub fn from_string(other: StdString) -> Self {
        Self {
            inner: KStringInner::from_string(other),
        }
    }

    /// Create an owned `KStringBase` optimally from a reference.
    #[inline]
    #[must_use]
    pub fn from_ref(other: &str) -> Self {
        Self {
            inner: KStringInner::from_ref(other),
        }
    }

    /// Get a reference to the `KStringBase`.
    #[inline]
    #[must_use]
    pub fn as_ref(&self) -> KStringRef<'_> {
        self.inner.as_ref()
    }

    /// Extracts a string slice containing the entire `KStringBase`.
    #[inline]
    #[must_use]
    pub fn as_str(&self) -> &str {
        self.inner.as_str()
    }

    /// Convert to a mutable string type, cloning the data if necessary.
    #[inline]
    #[must_use]
    pub fn into_string(self) -> StdString {
        String::from(self.into_boxed_str())
    }

    /// Convert to a mutable string type, cloning the data if necessary.
    #[inline]
    #[must_use]
    pub fn into_boxed_str(self) -> crate::backend::BoxedStr {
        self.inner.into_boxed_str()
    }

    /// Convert to a Cow str
    #[inline]
    #[must_use]
    pub fn into_cow_str(self) -> Cow<'static, str> {
        self.inner.into_cow_str()
    }
}

impl<B: crate::backend::HeapStr> std::ops::Deref for KStringBase<B> {
    type Target = str;

    #[inline]
    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl<B: crate::backend::HeapStr> Eq for KStringBase<B> {}

impl<'s, B: crate::backend::HeapStr> PartialEq<KStringBase<B>> for KStringBase<B> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        PartialEq::eq(self.as_str(), other.as_str())
    }
}

impl<'s, B: crate::backend::HeapStr> PartialEq<str> for KStringBase<B> {
    #[inline]
    fn eq(&self, other: &str) -> bool {
        PartialEq::eq(self.as_str(), other)
    }
}

impl<'s, B: crate::backend::HeapStr> PartialEq<&'s str> for KStringBase<B> {
    #[inline]
    fn eq(&self, other: &&str) -> bool {
        PartialEq::eq(self.as_str(), *other)
    }
}

impl<'s, B: crate::backend::HeapStr> PartialEq<String> for KStringBase<B> {
    #[inline]
    fn eq(&self, other: &StdString) -> bool {
        PartialEq::eq(self.as_str(), other.as_str())
    }
}

impl<B: crate::backend::HeapStr> Ord for KStringBase<B> {
    #[inline]
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.as_str().cmp(other.as_str())
    }
}

impl<B: crate::backend::HeapStr> PartialOrd for KStringBase<B> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.as_str().partial_cmp(other.as_str())
    }
}

impl<B: crate::backend::HeapStr> std::hash::Hash for KStringBase<B> {
    #[inline]
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.as_str().hash(state);
    }
}

impl<B: crate::backend::HeapStr> fmt::Debug for KStringBase<B> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.as_str().fmt(f)
    }
}

impl<B: crate::backend::HeapStr> fmt::Display for KStringBase<B> {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self.as_str(), f)
    }
}

impl<B: crate::backend::HeapStr> AsRef<str> for KStringBase<B> {
    #[inline]
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl<B: crate::backend::HeapStr> AsRef<[u8]> for KStringBase<B> {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        self.as_bytes()
    }
}

impl<B: crate::backend::HeapStr> AsRef<std::ffi::OsStr> for KStringBase<B> {
    #[inline]
    fn as_ref(&self) -> &std::ffi::OsStr {
        (&**self).as_ref()
    }
}

impl<B: crate::backend::HeapStr> AsRef<std::path::Path> for KStringBase<B> {
    #[inline]
    fn as_ref(&self) -> &std::path::Path {
        std::path::Path::new(self)
    }
}

impl<B: crate::backend::HeapStr> std::borrow::Borrow<str> for KStringBase<B> {
    #[inline]
    fn borrow(&self) -> &str {
        self.as_str()
    }
}

impl<B: crate::backend::HeapStr> Default for KStringBase<B> {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

impl<'s, B: crate::backend::HeapStr> From<KStringRef<'s>> for KStringBase<B> {
    #[inline]
    fn from(other: KStringRef<'s>) -> Self {
        other.to_owned()
    }
}

impl<'s, B: crate::backend::HeapStr> From<&'s KStringRef<'s>> for KStringBase<B> {
    #[inline]
    fn from(other: &'s KStringRef<'s>) -> Self {
        other.to_owned()
    }
}

impl<'s, B: crate::backend::HeapStr> From<KStringCowBase<'s, B>> for KStringBase<B> {
    #[inline]
    fn from(other: KStringCowBase<'s, B>) -> Self {
        other.into_owned()
    }
}

impl<'s, B: crate::backend::HeapStr> From<&'s KStringCowBase<'s, B>> for KStringBase<B> {
    #[inline]
    fn from(other: &'s KStringCowBase<'s, B>) -> Self {
        other.clone().into_owned()
    }
}

impl<B: crate::backend::HeapStr> From<StdString> for KStringBase<B> {
    #[inline]
    fn from(other: StdString) -> Self {
        Self::from_string(other)
    }
}

impl<'s, B: crate::backend::HeapStr> From<&'s StdString> for KStringBase<B> {
    #[inline]
    fn from(other: &'s StdString) -> Self {
        Self::from_ref(other)
    }
}

impl<B: crate::backend::HeapStr> From<crate::backend::BoxedStr> for KStringBase<B> {
    #[inline]
    fn from(other: crate::backend::BoxedStr) -> Self {
        Self::from_boxed(other)
    }
}

impl<'s, B: crate::backend::HeapStr> From<&'s crate::backend::BoxedStr> for KStringBase<B> {
    #[inline]
    fn from(other: &'s crate::backend::BoxedStr) -> Self {
        Self::from_ref(other)
    }
}

impl<B: crate::backend::HeapStr> From<&'static str> for KStringBase<B> {
    #[inline]
    fn from(other: &'static str) -> Self {
        Self::from_static(other)
    }
}

impl<B: crate::backend::HeapStr> std::str::FromStr for KStringBase<B> {
    type Err = std::convert::Infallible;
    #[inline]
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(Self::from_ref(s))
    }
}

#[cfg(feature = "serde")]
impl<B: crate::backend::HeapStr> serde::Serialize for KStringBase<B> {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(self.as_str())
    }
}

#[cfg(feature = "serde")]
impl<'de, B: crate::backend::HeapStr> serde::Deserialize<'de> for KStringBase<B> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_string(StringVisitor(std::marker::PhantomData))
    }
}

#[cfg(feature = "serde")]
struct StringVisitor<B>(std::marker::PhantomData<B>);

#[cfg(feature = "serde")]
impl<'de, B: crate::backend::HeapStr> serde::de::Visitor<'de> for StringVisitor<B> {
    type Value = KStringBase<B>;

    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter.write_str("a string")
    }

    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        Ok(Self::Value::from_ref(v))
    }

    fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        Ok(Self::Value::from_string(v))
    }

    fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        match std::str::from_utf8(v) {
            Ok(s) => Ok(Self::Value::from_ref(s)),
            Err(_) => Err(serde::de::Error::invalid_value(
                serde::de::Unexpected::Bytes(v),
                &self,
            )),
        }
    }

    fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
    where
        E: serde::de::Error,
    {
        match String::from_utf8(v) {
            Ok(s) => Ok(Self::Value::from_string(s)),
            Err(e) => Err(serde::de::Error::invalid_value(
                serde::de::Unexpected::Bytes(&e.into_bytes()),
                &self,
            )),
        }
    }
}

use inner::KStringInner;

#[cfg(not(feature = "unsafe"))]
mod inner {
    use super::*;

    pub(super) enum KStringInner<B> {
        Singleton(&'static str),
        Inline(StackString<CAPACITY>),
        Owned(B),
    }

    impl<B> KStringInner<B> {
        /// Create a reference to a `'static` data.
        #[inline]
        pub const fn from_static(other: &'static str) -> Self {
            Self::Singleton(other)
        }

        #[inline]
        pub fn try_inline(other: &str) -> Option<Self> {
            StackString::try_new(other).map(Self::Inline)
        }
    }

    impl<B: crate::backend::HeapStr> KStringInner<B> {
        #[inline]
        pub(super) fn from_boxed(other: crate::backend::BoxedStr) -> Self {
            #[allow(clippy::useless_conversion)]
            Self::Owned(B::from_boxed_str(other))
        }

        #[inline]
        pub(super) fn from_string(other: StdString) -> Self {
            if (0..=CAPACITY).contains(&other.len()) {
                let inline = { StackString::new(other.as_str()) };
                Self::Inline(inline)
            } else {
                Self::from_boxed(other.into_boxed_str())
            }
        }

        #[inline]
        pub(super) fn from_ref(other: &str) -> Self {
            if (0..=CAPACITY).contains(&other.len()) {
                let inline = { StackString::new(other) };
                Self::Inline(inline)
            } else {
                Self::Owned(B::from_str(other))
            }
        }

        #[inline]
        pub(super) fn as_ref(&self) -> KStringRef<'_> {
            match self {
                Self::Singleton(s) => KStringRef::from_static(s),
                Self::Inline(s) => KStringRef::from_ref(s.as_str()),
                Self::Owned(s) => KStringRef::from_ref(s.as_str()),
            }
        }

        #[inline]
        pub(super) fn as_str(&self) -> &str {
            match self {
                Self::Singleton(s) => s,
                Self::Inline(s) => s.as_str(),
                Self::Owned(s) => s.as_str(),
            }
        }

        #[inline]
        pub(super) fn into_boxed_str(self) -> crate::backend::BoxedStr {
            match self {
                Self::Singleton(s) => crate::backend::BoxedStr::from(s),
                Self::Inline(s) => crate::backend::BoxedStr::from(s.as_str()),
                Self::Owned(s) => crate::backend::BoxedStr::from(s.as_str()),
            }
        }

        /// Convert to a Cow str
        #[inline]
        pub(super) fn into_cow_str(self) -> Cow<'static, str> {
            match self {
                Self::Singleton(s) => Cow::Borrowed(s),
                Self::Inline(s) => Cow::Owned(s.as_str().into()),
                Self::Owned(s) => Cow::Owned(s.as_str().into()),
            }
        }
    }

    // Explicit to avoid inlining which cuts clone times in half.
    //
    // An automatically derived `clone()` has 10ns overhead while the explicit `Deref`/`as_str` has
    // none of that.  Being explicit and removing the `#[inline]` attribute dropped the overhead to
    // 5ns.
    //
    // My only guess is that the `clone()` calls we delegate to are just that much bigger than
    // `as_str()` that, when combined with a jump table, is blowing the icache, slowing things down.
    impl<B: Clone> Clone for KStringInner<B> {
        fn clone(&self) -> Self {
            match self {
                Self::Singleton(s) => Self::Singleton(s),
                Self::Inline(s) => Self::Inline(*s),
                Self::Owned(s) => Self::Owned(s.clone()),
            }
        }
    }

    #[allow(unused)]
    const LEN_SIZE: usize = std::mem::size_of::<crate::stack::Len>();

    #[allow(unused)]
    const TAG_SIZE: usize = std::mem::size_of::<u8>();

    #[allow(unused)]
    const MAX_CAPACITY: usize =
        std::mem::size_of::<crate::string::StdString>() - TAG_SIZE - LEN_SIZE;

    // Performance seems to slow down when trying to occupy all of the padding left by `String`'s
    // discriminant.  The question is whether faster len=1-16 "allocations" outweighs going to the heap
    // for len=17-22.
    #[allow(unused)]
    const ALIGNED_CAPACITY: usize = std::mem::size_of::<crate::backend::DefaultStr>() - LEN_SIZE;

    #[cfg(feature = "max_inline")]
    const CAPACITY: usize = MAX_CAPACITY;
    #[cfg(not(feature = "max_inline"))]
    const CAPACITY: usize = ALIGNED_CAPACITY;
}

#[cfg(feature = "unsafe")]
mod inner {
    use super::*;

    pub(super) union KStringInner<B> {
        tag: TagVariant,
        singleton: SingletonVariant,
        owned: std::mem::ManuallyDrop<OwnedVariant<B>>,
        inline: InlineVariant,
    }

    impl<B> KStringInner<B> {
        /// Create a reference to a `'static` data.
        #[inline]
        pub const fn from_static(other: &'static str) -> Self {
            Self {
                singleton: SingletonVariant::new(other),
            }
        }

        #[inline]
        pub fn try_inline(other: &str) -> Option<Self> {
            StackString::try_new(other).map(|inline| Self {
                inline: InlineVariant::new(inline),
            })
        }

        #[inline]
        const fn tag(&self) -> Tag {
            unsafe {
                // SAFETY: `tag` is in the same spot in each variant
                self.tag.tag
            }
        }
    }

    impl<B: crate::backend::HeapStr> KStringInner<B> {
        #[inline]
        pub(super) fn from_boxed(other: crate::backend::BoxedStr) -> Self {
            #[allow(clippy::useless_conversion)]
            let payload = B::from_boxed_str(other);
            Self {
                owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
            }
        }

        #[inline]
        pub(super) fn from_string(other: StdString) -> Self {
            if (0..=CAPACITY).contains(&other.len()) {
                let payload = unsafe {
                    // SAFETY: range check ensured this is always safe
                    StackString::new_unchecked(other.as_str())
                };
                Self {
                    inline: InlineVariant::new(payload),
                }
            } else {
                Self::from_boxed(other.into_boxed_str())
            }
        }

        #[inline]
        pub(super) fn from_ref(other: &str) -> Self {
            if (0..=CAPACITY).contains(&other.len()) {
                let payload = unsafe {
                    // SAFETY: range check ensured this is always safe
                    StackString::new_unchecked(other)
                };
                Self {
                    inline: InlineVariant::new(payload),
                }
            } else {
                #[allow(clippy::useless_conversion)]
                let payload = B::from_str(other);
                Self {
                    owned: std::mem::ManuallyDrop::new(OwnedVariant::new(payload)),
                }
            }
        }

        #[inline]
        pub(super) fn as_ref(&self) -> KStringRef<'_> {
            let tag = self.tag();
            unsafe {
                // SAFETY: `tag` ensures access to correct variant
                if tag.is_singleton() {
                    KStringRef::from_static(self.singleton.payload)
                } else if tag.is_owned() {
                    KStringRef::from_ref(self.owned.payload.as_str())
                } else {
                    debug_assert!(tag.is_inline());
                    KStringRef::from_ref(self.inline.payload.as_str())
                }
            }
        }

        #[inline]
        pub(super) fn as_str(&self) -> &str {
            let tag = self.tag();
            unsafe {
                // SAFETY: `tag` ensures access to correct variant
                if tag.is_singleton() {
                    self.singleton.payload
                } else if tag.is_owned() {
                    self.owned.payload.as_str()
                } else {
                    debug_assert!(tag.is_inline());
                    self.inline.payload.as_str()
                }
            }
        }

        #[inline]
        pub(super) fn into_boxed_str(self) -> crate::backend::BoxedStr {
            let tag = self.tag();
            unsafe {
                // SAFETY: `tag` ensures access to correct variant
                if tag.is_singleton() {
                    crate::backend::BoxedStr::from(self.singleton.payload)
                } else if tag.is_owned() {
                    crate::backend::BoxedStr::from(self.owned.payload.as_str())
                } else {
                    debug_assert!(tag.is_inline());
                    crate::backend::BoxedStr::from(self.inline.payload.as_ref())
                }
            }
        }

        /// Convert to a Cow str
        #[inline]
        pub(super) fn into_cow_str(self) -> Cow<'static, str> {
            let tag = self.tag();
            unsafe {
                // SAFETY: `tag` ensures access to correct variant
                if tag.is_singleton() {
                    Cow::Borrowed(self.singleton.payload)
                } else if tag.is_owned() {
                    Cow::Owned(self.owned.payload.as_str().into())
                } else {
                    debug_assert!(tag.is_inline());
                    Cow::Owned(self.inline.payload.as_str().into())
                }
            }
        }
    }

    // Explicit to avoid inlining which cuts clone times in half.
    //
    // An automatically derived `clone()` has 10ns overhead while the explicit `Deref`/`as_str` has
    // none of that.  Being explicit and removing the `#[inline]` attribute dropped the overhead to
    // 5ns.
    //
    // My only guess is that the `clone()` calls we delegate to are just that much bigger than
    // `as_str()` that, when combined with a jump table, is blowing the icache, slowing things down.
    impl<B: Clone> Clone for KStringInner<B> {
        fn clone(&self) -> Self {
            let tag = self.tag();
            if tag.is_owned() {
                unsafe {
                    // SAFETY: `tag` ensures access to correct variant
                    Self {
                        owned: std::mem::ManuallyDrop::new(OwnedVariant::new(
                            self.owned.payload.clone(),
                        )),
                    }
                }
            } else {
                unsafe {
                    // SAFETY: `tag` ensures access to correct variant
                    // SAFETY: non-owned types are copyable
                    std::mem::transmute_copy(self)
                }
            }
        }
    }

    impl<B> Drop for KStringInner<B> {
        fn drop(&mut self) {
            let tag = self.tag();
            if tag.is_owned() {
                unsafe {
                    // SAFETY: `tag` ensures we are using the right variant
                    std::mem::ManuallyDrop::drop(&mut self.owned)
                }
            }
        }
    }

    #[allow(unused)]
    const LEN_SIZE: usize = std::mem::size_of::<crate::stack::Len>();

    #[allow(unused)]
    const TAG_SIZE: usize = std::mem::size_of::<Tag>();

    #[allow(unused)]
    const PAYLOAD_SIZE: usize = std::mem::size_of::<crate::backend::DefaultStr>();
    type Payload = Padding<PAYLOAD_SIZE>;

    #[allow(unused)]
    const TARGET_SIZE: usize = std::mem::size_of::<Target>();
    type Target = crate::string::StdString;

    #[allow(unused)]
    const MAX_CAPACITY: usize = TARGET_SIZE - LEN_SIZE - TAG_SIZE;

    // Performance seems to slow down when trying to occupy all of the padding left by `String`'s
    // discriminant.  The question is whether faster len=1-16 "allocations" outweighs going to the heap
    // for len=17-22.
    #[allow(unused)]
    const ALIGNED_CAPACITY: usize = PAYLOAD_SIZE - LEN_SIZE;

    #[cfg(feature = "max_inline")]
    const CAPACITY: usize = MAX_CAPACITY;
    #[cfg(not(feature = "max_inline"))]
    const CAPACITY: usize = ALIGNED_CAPACITY;

    const PAYLOAD_PAD_SIZE: usize = TARGET_SIZE - PAYLOAD_SIZE - TAG_SIZE;
    const INLINE_PAD_SIZE: usize = TARGET_SIZE - CAPACITY - LEN_SIZE - TAG_SIZE;

    #[derive(Copy, Clone)]
    #[repr(C)]
    struct TagVariant {
        payload: Payload,
        pad: Padding<PAYLOAD_PAD_SIZE>,
        tag: Tag,
    }
    static_assertions::assert_eq_size!(Target, TagVariant);

    #[derive(Copy, Clone)]
    #[repr(C)]
    struct SingletonVariant {
        payload: &'static str,
        pad: Padding<PAYLOAD_PAD_SIZE>,
        tag: Tag,
    }
    static_assertions::assert_eq_size!(Payload, &'static str);
    static_assertions::assert_eq_size!(Target, SingletonVariant);

    impl SingletonVariant {
        #[inline]
        const fn new(payload: &'static str) -> Self {
            Self {
                payload,
                pad: Padding::new(),
                tag: Tag::SINGLETON,
            }
        }
    }

    impl std::fmt::Debug for SingletonVariant {
        #[inline]
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            self.payload.fmt(f)
        }
    }

    #[derive(Clone)]
    #[repr(C)]
    struct OwnedVariant<B> {
        payload: B,
        pad: Padding<PAYLOAD_PAD_SIZE>,
        tag: Tag,
    }
    static_assertions::assert_eq_size!(Payload, crate::backend::DefaultStr);
    static_assertions::assert_eq_size!(Target, OwnedVariant<crate::backend::DefaultStr>);

    impl<B> OwnedVariant<B> {
        #[inline]
        const fn new(payload: B) -> Self {
            Self {
                payload,
                pad: Padding::new(),
                tag: Tag::OWNED,
            }
        }
    }

    impl<B: crate::backend::HeapStr> std::fmt::Debug for OwnedVariant<B> {
        #[inline]
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            self.payload.fmt(f)
        }
    }

    #[derive(Copy, Clone)]
    #[repr(C)]
    struct InlineVariant {
        payload: StackString<CAPACITY>,
        pad: Padding<INLINE_PAD_SIZE>,
        tag: Tag,
    }
    static_assertions::assert_eq_size!(Target, InlineVariant);

    impl InlineVariant {
        #[inline]
        const fn new(payload: StackString<CAPACITY>) -> Self {
            Self {
                payload,
                pad: Padding::new(),
                tag: Tag::INLINE,
            }
        }
    }

    impl std::fmt::Debug for InlineVariant {
        #[inline]
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            self.payload.fmt(f)
        }
    }

    #[derive(Copy, Clone, PartialEq, Eq)]
    #[repr(transparent)]
    struct Tag(u8);

    impl Tag {
        const SINGLETON: Tag = Tag(0);
        const OWNED: Tag = Tag(u8::MAX);
        const INLINE: Tag = Tag(1);

        #[inline]
        const fn is_singleton(self) -> bool {
            self.0 == Self::SINGLETON.0
        }

        #[inline]
        const fn is_owned(self) -> bool {
            self.0 == Self::OWNED.0
        }

        #[inline]
        const fn is_inline(self) -> bool {
            !self.is_singleton() && !self.is_owned()
        }
    }

    #[derive(Copy, Clone)]
    #[repr(transparent)]
    struct Padding<const L: usize>([std::mem::MaybeUninit<u8>; L]);

    impl<const L: usize> Padding<L> {
        const fn new() -> Self {
            let padding = unsafe {
                // SAFETY: Padding, never actually used
                std::mem::MaybeUninit::uninit().assume_init()
            };
            Self(padding)
        }
    }

    impl<const L: usize> Default for Padding<L> {
        fn default() -> Self {
            Self::new()
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_size() {
        println!("KString: {}", std::mem::size_of::<KString>());
    }
}
