use super::{Transform, Utf8Policy};
use std::{
borrow::Cow,
ops::Deref,
sync::atomic::{AtomicI8, Ordering::Relaxed},
};
pub const DISPLAY_PLACEHOLDER: &str = "<?>";
#[derive(Default)]
pub struct Bytes<'a> {
value: &'a [u8],
ownership: Option<OwnedBytes>,
utf8: AtomicI8,
secret: bool,
}
impl Bytes<'static> {
pub fn to_utf8_lossy_static(&self) -> Cow<'static, str> {
if self.is_owning() {
Cow::Owned(self.to_utf8_lossy().into_owned())
} else {
unsafe { self.utf8_cow() }
}
}
}
unsafe impl<'a> crate::owning::MakeOwning for crate::string::Bytes<'a> {
type This<'b> = crate::string::Bytes<'b>;
fn make_owning(&mut self) {
if !self.is_owning() {
self.owning_force(false);
}
}
}
impl<'a> Bytes<'a> {
pub const fn empty() -> Bytes<'a> {
Bytes { value: &[], ownership: None, utf8: AtomicI8::new(1), secret: false }
}
pub const fn from_bytes(value: &'a [u8]) -> Self {
Bytes { value, ownership: None, utf8: AtomicI8::new(0), secret: false }
}
pub const fn from_str(value: &'a str) -> Self {
Bytes { value: value.as_bytes(), ownership: None, utf8: AtomicI8::new(1), secret: false }
}
pub fn from_secret(value: Vec<u8>) -> Self {
let (ownership, value) = unsafe { OwnedBytes::from_vec(value) };
Bytes { value, ownership, utf8: AtomicI8::new(0), secret: true }
}
pub const fn is_owning(&self) -> bool {
self.ownership.is_some()
}
pub fn is_secret(&self) -> bool {
self.secret
}
pub fn owning<'b>(mut self) -> Bytes<'b> {
if !self.is_owning() {
let secret = self.secret;
self.owning_force(secret);
}
unsafe { std::mem::transmute(self) }
}
pub fn secret(mut self) -> Self {
if !self.secret && self.is_owning() {
self.owning_force(true);
} else {
self.secret = true;
}
self
}
fn owning_force(&mut self, secret: bool) {
unsafe {
let (ownership, value) = OwnedBytes::from_vec(self.value.to_vec());
*self = Bytes { value, ownership, utf8: self.utf8.load(Relaxed).into(), secret }
}
}
pub const fn is_empty(&self) -> bool {
self.value.is_empty()
}
pub const fn len(&self) -> usize {
self.value.len()
}
#[inline]
pub fn is_utf8_lazy(&self) -> Option<bool> {
match self.utf8.load(Relaxed) {
1 => Some(true),
-1 => Some(false),
_ => None,
}
}
pub fn to_utf8(&self) -> Option<&str> {
match self.is_utf8_lazy() {
Some(true) => Some(unsafe { std::str::from_utf8_unchecked(self.value) }),
Some(false) => None,
None => {
let so = std::str::from_utf8(self.value).ok();
let utf8 = if so.is_some() { 1i8 } else { -1i8 };
self.utf8.store(utf8, Relaxed);
so
}
}
}
pub fn to_utf8_lossy(&self) -> Cow<'_, str> {
unsafe { self.utf8_cow() }
}
pub fn into_utf8_lossy(self) -> Self {
match unsafe { self.utf8_cow() } {
Cow::Borrowed(s) => Bytes {
value: s.as_bytes(),
ownership: self.ownership,
utf8: 1i8.into(),
secret: self.secret,
},
Cow::Owned(o) => o.into(),
}
}
#[cfg(feature = "base64")]
fn to_base64_impl(&self) -> Bytes<'static> {
use base64::engine::{general_purpose::STANDARD as ENGINE, Engine};
let encoded = ENGINE.encode(self.value);
unsafe {
let (ownership, value) = OwnedBytes::from_vec(encoded.into_bytes());
Bytes { value, ownership, utf8: 1i8.into(), secret: self.secret }
}
}
#[cfg(feature = "base64")]
pub fn to_base64(&self) -> super::Word<'static> {
unsafe { super::Word::from_unchecked(self.to_base64_impl()) }
}
#[cfg(feature = "base64")]
pub fn to_base64_plus(&self) -> super::Arg<'static> {
if self.is_empty() {
crate::names::PLUS
} else {
unsafe { super::Arg::from_unchecked(self.to_base64_impl()) }
}
}
unsafe fn utf8_cow(&self) -> Cow<'a, str> {
match self.is_utf8_lazy() {
Some(true) => Cow::Borrowed(unsafe { std::str::from_utf8_unchecked(self.value) }),
Some(false) => String::from_utf8_lossy(self.value),
None => {
let sl = String::from_utf8_lossy(self.value);
let utf8 = if matches!(&sl, Cow::Borrowed(_)) { 1i8 } else { -1i8 };
self.utf8.store(utf8, Relaxed);
sl
}
}
}
fn utf8_for_policy(&self, utf8: Utf8Policy) -> i8 {
match utf8 {
Utf8Policy::PreserveStrict => self.utf8.load(Relaxed),
Utf8Policy::Preserve => (self.utf8.load(Relaxed) == 1) as i8,
Utf8Policy::Invalid | Utf8Policy::Recheck | Utf8Policy::Valid => utf8 as i8,
}
}
pub const fn as_bytes(&self) -> &[u8] {
self.value
}
pub const unsafe fn as_bytes_unsafe(&self) -> &'a [u8] {
self.value
}
pub unsafe fn using_value(&self, value: &'a [u8], utf8: Utf8Policy) -> Self {
let utf8 = self.utf8_for_policy(utf8);
let ownership = if value.is_empty() { None } else { self.ownership.clone() };
Bytes { value, ownership, utf8: utf8.into(), secret: self.secret }
}
pub fn transform<T: Transform>(&mut self, tf: T) -> T::Value {
let tfed = tf.transform(self);
if tfed.transformed.as_ref().is_empty() {
*self = Bytes::empty();
return tfed.value;
}
match tfed.transformed {
Cow::Borrowed(s) => {
match tfed.utf8 {
Utf8Policy::PreserveStrict => (),
Utf8Policy::Preserve => {
let _ = self.utf8.compare_exchange(-1i8, 0i8, Relaxed, Relaxed);
}
Utf8Policy::Invalid | Utf8Policy::Recheck | Utf8Policy::Valid => {
self.utf8.store(tfed.utf8 as i8, Relaxed);
}
}
self.value = s;
}
Cow::Owned(o) => {
let utf8 = self.utf8_for_policy(tfed.utf8);
unsafe {
let (ownership, value) = OwnedBytes::from_vec(o);
*self = Bytes { value, ownership, utf8: utf8.into(), secret: self.secret };
}
}
}
tfed.value
}
pub fn into_vec(self) -> Vec<u8> {
if let Some(owner) = self.ownership {
unsafe { owner.into_vec(self.value, self.secret) }
} else {
self.value.to_vec()
}
}
}
impl<'a> Deref for Bytes<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.value
}
}
impl AsRef<[u8]> for Bytes<'_> {
fn as_ref(&self) -> &[u8] {
self.value
}
}
impl std::borrow::Borrow<[u8]> for Bytes<'_> {
fn borrow(&self) -> &[u8] {
self.value
}
}
impl<'a> From<Vec<u8>> for Bytes<'a> {
fn from(value: Vec<u8>) -> Self {
unsafe {
let (ownership, value) = OwnedBytes::from_vec(value);
Bytes { value, ownership, utf8: 0i8.into(), secret: false }
}
}
}
impl<'a> From<String> for Bytes<'a> {
fn from(value: String) -> Self {
unsafe {
let (ownership, value) = OwnedBytes::from_vec(value.into_bytes());
Bytes { value, ownership, utf8: 1i8.into(), secret: false }
}
}
}
impl<'a> From<&'a [u8]> for Bytes<'a> {
fn from(value: &'a [u8]) -> Self {
value.to_vec().into()
}
}
impl<'a> From<&'a str> for Bytes<'a> {
fn from(value: &'a str) -> Self {
value.to_owned().into()
}
}
impl<'a> From<Cow<'a, [u8]>> for Bytes<'a> {
fn from(value: Cow<'a, [u8]>) -> Self {
match value {
Cow::Borrowed(s) => s.into(),
Cow::Owned(s) => s.into(),
}
}
}
impl<'a> From<Cow<'a, str>> for Bytes<'a> {
fn from(value: Cow<'a, str>) -> Self {
match value {
Cow::Borrowed(s) => s.into(),
Cow::Owned(s) => s.into(),
}
}
}
impl<'a> From<Bytes<'a>> for Vec<u8> {
fn from(value: Bytes<'a>) -> Self {
value.into_vec()
}
}
impl<'a> From<Bytes<'a>> for Cow<'a, [u8]> {
fn from(value: Bytes<'a>) -> Self {
match value.ownership {
Some(v) => Cow::Owned(unsafe { v.into_vec(value.value, value.secret) }),
None => Cow::Borrowed(value.value),
}
}
}
impl Clone for Bytes<'_> {
fn clone(&self) -> Self {
Bytes {
value: self.value,
ownership: self.ownership.clone(),
utf8: self.utf8.load(Relaxed).into(),
secret: self.secret,
}
}
}
impl PartialEq for Bytes<'_> {
fn eq(&self, b: &Bytes<'_>) -> bool {
self.value == b.value
}
}
impl<const N: usize> PartialEq<[u8; N]> for Bytes<'_> {
fn eq(&self, other: &[u8; N]) -> bool {
self.value == other.as_slice()
}
}
impl<const N: usize> PartialEq<&[u8; N]> for Bytes<'_> {
fn eq(&self, other: &&[u8; N]) -> bool {
self == *other
}
}
impl PartialEq<[u8]> for Bytes<'_> {
fn eq(&self, other: &[u8]) -> bool {
self.value == other
}
}
impl PartialEq<&[u8]> for Bytes<'_> {
fn eq(&self, other: &&[u8]) -> bool {
self == *other
}
}
impl PartialEq<str> for Bytes<'_> {
fn eq(&self, other: &str) -> bool {
self.utf8.load(Relaxed) != -1 && self.value == other.as_bytes()
}
}
impl PartialEq<&str> for Bytes<'_> {
fn eq(&self, other: &&str) -> bool {
self == *other
}
}
impl Eq for Bytes<'_> {}
impl PartialOrd for Bytes<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Bytes<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(other.value)
}
}
impl std::hash::Hash for Bytes<'_> {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
self.value.hash(hasher);
}
}
impl std::fmt::Display for Bytes<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_secret() {
f.write_str(DISPLAY_PLACEHOLDER)
} else if let Some(s) = self.to_utf8() {
f.write_str(s)
} else {
f.write_str(DISPLAY_PLACEHOLDER)
}
}
}
impl std::fmt::Debug for Bytes<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = f.debug_struct("Bytes");
let f = f.field("owning", &self.is_owning());
if !self.is_secret() {
if let Some(v) = self.to_utf8() {
f.field("value", &v)
} else {
f.field("value", &self.value)
}
} else {
f.field("value", &DISPLAY_PLACEHOLDER)
}
.finish()
}
}
#[repr(transparent)]
#[derive(Clone)]
struct OwnedBytes(crate::util::ThinArc<crate::util::OwnedSlice<u8>>);
impl OwnedBytes {
pub unsafe fn from_vec<'a>(value: Vec<u8>) -> (Option<Self>, &'a [u8]) {
if value.is_empty() {
return (None, &[]);
}
let (os, len) = crate::util::OwnedSlice::from_vec(value);
let slice = unsafe { os.as_slice(len) };
(Some(OwnedBytes(crate::util::ThinArc::new(os))), slice)
}
pub unsafe fn into_vec(self, slice: &[u8], secret: bool) -> Vec<u8> {
if let Ok(os) = self.0.try_unwrap() {
let (retval, destroy) = os.into_vec_with_slice(slice);
if secret {
if let Some(mut destroy) = destroy {
destroy.reinit_all();
}
}
retval
} else {
slice.to_vec()
}
}
}
unsafe impl Send for OwnedBytes {}
unsafe impl Sync for OwnedBytes {}