mod binary;
mod map;
mod number;
mod option;
mod seq;
mod text;
pub mod tags;
use crate::{
std::{
borrow::Borrow,
cmp::Ordering,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
},
Result, Stream, Value,
};
#[cfg(feature = "alloc")]
use crate::std::boxed::Box;
pub use self::{binary::*, map::*, number::*, option::*, text::*};
pub struct Label<'computed> {
value_computed: *const str,
backing_field_static: Option<&'static str>,
#[cfg(feature = "alloc")]
backing_field_owned: Option<*mut str>,
tag: Option<Tag>,
_marker: PhantomData<&'computed str>,
}
impl<'computed> Drop for Label<'computed> {
fn drop(&mut self) {
#[cfg(feature = "alloc")]
{
if let Some(owned) = self.backing_field_owned {
drop(unsafe { Box::from_raw(owned) });
}
}
}
}
unsafe impl<'computed> Send for Label<'computed> {}
unsafe impl<'computed> Sync for Label<'computed> {}
#[cfg(not(feature = "alloc"))]
impl<'computed> Clone for Label<'computed> {
fn clone(&self) -> Self {
Label {
value_computed: self.value_computed,
backing_field_static: self.backing_field_static,
tag: self.tag.clone(),
_marker: PhantomData,
}
}
}
impl<'computed> Label<'computed> {
#[inline(always)]
pub const fn new(label: &'static str) -> Self {
Label {
value_computed: label as *const str,
backing_field_static: Some(label),
#[cfg(feature = "alloc")]
backing_field_owned: None,
tag: None,
_marker: PhantomData,
}
}
#[inline(always)]
pub const fn new_computed(label: &'computed str) -> Self {
Label {
value_computed: label as *const str,
backing_field_static: None,
#[cfg(feature = "alloc")]
backing_field_owned: None,
tag: None,
_marker: PhantomData,
}
}
#[inline(always)]
pub const fn as_str(&self) -> &str {
unsafe { &*self.value_computed }
}
#[inline(always)]
pub const fn as_static_str(&self) -> Option<&'static str> {
self.backing_field_static
}
#[inline(always)]
pub const fn with_tag(mut self, tag: &Tag) -> Self {
self.tag = Some(tag.cloned());
self
}
#[inline(always)]
pub const fn tag(&self) -> Option<&Tag> {
self.tag.as_ref()
}
#[inline(always)]
pub fn try_to_owned(&self) -> Result<Label<'static>> {
#[cfg(feature = "alloc")]
{
Ok(self.to_owned())
}
#[cfg(not(feature = "alloc"))]
{
self.as_static_str()
.map(Label::new)
.ok_or_else(crate::Error::new)
}
}
}
impl<'a, 'b> PartialEq<Label<'b>> for Label<'a> {
#[inline(always)]
fn eq(&self, other: &Label<'b>) -> bool {
self.as_str() == other.as_str()
}
}
impl<'a> Eq for Label<'a> {}
impl<'a> Hash for Label<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}
impl<'a> Borrow<str> for Label<'a> {
#[inline(always)]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<'a> fmt::Debug for Label<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Label").field(&self.as_str()).finish()
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Tag {
id: u64,
data: &'static str,
}
impl Tag {
pub const fn new(data: &'static str) -> Self {
const fn compute_id(bytes: &[u8]) -> u64 {
const K: u64 = 0x517cc1b727220a95u64;
let mut hash = 0u64;
let mut b = 0;
while b + 8 <= bytes.len() {
let i = [
bytes[b + 0],
bytes[b + 1],
bytes[b + 2],
bytes[b + 3],
bytes[b + 4],
bytes[b + 5],
bytes[b + 6],
bytes[b + 7],
];
let i = u64::from_ne_bytes(i);
hash = (hash.rotate_left(5) ^ i).wrapping_mul(K);
b += 8;
}
if b + 4 <= bytes.len() {
let i = [bytes[b + 0], bytes[b + 1], bytes[b + 2], bytes[b + 3]];
let i = u32::from_ne_bytes(i) as u64;
hash = (hash.rotate_left(5) ^ i).wrapping_mul(K);
b += 4;
}
if b + 2 <= bytes.len() {
let i = [bytes[b + 0], bytes[b + 1]];
let i = u16::from_ne_bytes(i) as u64;
hash = (hash.rotate_left(5) ^ i).wrapping_mul(K);
b += 2;
}
if b + 1 <= bytes.len() {
let i = bytes[b + 0] as u64;
hash = (hash.rotate_left(5) ^ i).wrapping_mul(K);
}
hash
}
Tag {
id: compute_id(data.as_bytes()),
data,
}
}
#[inline(always)]
const fn cloned(&self) -> Tag {
Tag {
id: self.id,
data: self.data,
}
}
}
impl fmt::Debug for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("Tag").field(&self.data).finish()
}
}
#[derive(Clone)]
pub struct Index(i128, Option<Tag>);
impl From<i32> for Index {
#[inline(always)]
fn from(index: i32) -> Self {
Index::new_i32(index)
}
}
impl From<i64> for Index {
#[inline(always)]
fn from(index: i64) -> Self {
Index::new_i64(index)
}
}
impl From<isize> for Index {
#[inline(always)]
fn from(index: isize) -> Self {
Index::new_isize(index)
}
}
impl From<u32> for Index {
#[inline(always)]
fn from(index: u32) -> Self {
Index::new_u32(index)
}
}
impl From<u64> for Index {
#[inline(always)]
fn from(index: u64) -> Self {
Index::new_u64(index)
}
}
impl From<usize> for Index {
#[inline(always)]
fn from(index: usize) -> Self {
Index::new(index)
}
}
impl Index {
#[inline(always)]
pub const fn new(index: usize) -> Self {
Index(index as i128, None)
}
#[inline(always)]
pub const fn new_u32(index: u32) -> Self {
Index(index as i128, None)
}
#[inline(always)]
pub const fn new_u64(index: u64) -> Self {
Index(index as i128, None)
}
#[inline(always)]
pub const fn new_i32(index: i32) -> Self {
Index(index as i128, None)
}
#[inline(always)]
pub const fn new_i64(index: i64) -> Self {
Index(index as i128, None)
}
#[inline(always)]
pub const fn new_isize(index: isize) -> Self {
Index(index as i128, None)
}
#[inline]
pub const fn to_usize(&self) -> Option<usize> {
if self.0 >= usize::MIN as i128 && self.0 <= usize::MAX as i128 {
Some(self.0 as usize)
} else {
None
}
}
#[inline]
pub const fn to_u32(&self) -> Option<u32> {
if self.0 >= u32::MIN as i128 && self.0 <= u32::MAX as i128 {
Some(self.0 as u32)
} else {
None
}
}
#[inline]
pub const fn to_u64(&self) -> Option<u64> {
if self.0 >= u64::MIN as i128 && self.0 <= u64::MAX as i128 {
Some(self.0 as u64)
} else {
None
}
}
#[inline]
pub const fn to_isize(&self) -> Option<isize> {
if self.0 >= isize::MIN as i128 && self.0 <= isize::MAX as i128 {
Some(self.0 as isize)
} else {
None
}
}
#[inline]
pub const fn to_i32(&self) -> Option<i32> {
if self.0 >= i32::MIN as i128 && self.0 <= i32::MAX as i128 {
Some(self.0 as i32)
} else {
None
}
}
#[inline]
pub const fn to_i64(&self) -> Option<i64> {
if self.0 >= i64::MIN as i128 && self.0 <= i64::MAX as i128 {
Some(self.0 as i64)
} else {
None
}
}
#[inline(always)]
pub const fn with_tag(mut self, tag: &Tag) -> Self {
self.1 = Some(tag.cloned());
self
}
#[inline(always)]
pub const fn tag(&self) -> Option<&Tag> {
self.1.as_ref()
}
}
impl fmt::Debug for Index {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Index").field("value", &self.0).finish()
}
}
impl fmt::Display for Index {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl PartialEq for Index {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl Eq for Index {}
impl PartialOrd for Index {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl Hash for Index {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl Ord for Index {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl Value for () {
fn stream<'sval, S: Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> Result {
stream.tag(Some(&tags::RUST_UNIT), None, None)
}
}
impl Value for bool {
fn stream<'sval, S: Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> Result {
stream.bool(*self)
}
fn tag(&self) -> Option<Tag> {
None
}
fn to_bool(&self) -> Option<bool> {
Some(*self)
}
}
#[cfg(feature = "alloc")]
mod alloc_support {
use super::*;
use crate::std::{borrow::ToOwned, string::String};
impl<'computed> Clone for Label<'computed> {
fn clone(&self) -> Self {
if let Some(owned) = self.backing_field_owned {
Label::new_owned(unsafe { &*owned }.to_owned())
} else {
Label {
value_computed: self.value_computed,
backing_field_static: self.backing_field_static,
backing_field_owned: None,
tag: self.tag.clone(),
_marker: PhantomData,
}
}
}
}
impl<'computed> Label<'computed> {
pub fn to_owned(&self) -> Label<'static> {
if let Some(backing_field_static) = self.backing_field_static {
Label::new(backing_field_static)
} else {
Label::new_owned(self.as_str().into())
}
}
}
impl Label<'static> {
pub fn new_owned(label: String) -> Self {
let owned = Box::into_raw(label.into_boxed_str());
Label {
value_computed: owned as *const str,
backing_field_static: None,
backing_field_owned: Some(owned),
tag: None,
_marker: PhantomData,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unit_tag() {
assert_eq!(Some(tags::RUST_UNIT), ().tag());
}
#[test]
fn label_send_sync() {
fn assert<T: Send + Sync>() {}
assert::<Label>();
}
#[test]
fn label_static() {
let label = Label::new("a");
assert_eq!("a", label.as_static_str().unwrap());
assert_eq!("a", label.as_str());
}
#[test]
fn label_computed() {
let label = Label::new_computed("a");
assert!(label.as_static_str().is_none());
assert_eq!("a", label.as_str());
}
#[test]
fn label_tag() {
let label = Label::new("a").with_tag(&tags::VALUE_IDENT);
assert_eq!(Some(&tags::VALUE_IDENT), label.tag());
}
#[test]
fn label_eq() {
let a = Label::new("a");
let b = Label::new_computed("a");
let c = Label::new("b");
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn label_clone() {
let a = Label::new("a");
let b = a.clone();
assert_eq!(a.value_computed, b.value_computed);
assert_eq!(a.backing_field_static, b.backing_field_static);
}
#[test]
fn index_convert() {
for (index, to_i32, to_i64, to_u32, to_u64) in [
(
Index::from(0),
Some(0i32),
Some(0i64),
Some(0u32),
Some(0u64),
),
(
Index::from(i32::MIN),
Some(i32::MIN),
Some(i32::MIN as i64),
None,
None,
),
(Index::from(i64::MIN), None, Some(i64::MIN), None, None),
(
Index::from(u32::MAX),
None,
Some(u32::MAX as i64),
Some(u32::MAX),
Some(u32::MAX as u64),
),
(Index::from(u64::MAX), None, None, None, Some(u64::MAX)),
] {
assert_eq!(to_i32, index.to_i32(), "{:?}", index);
assert_eq!(to_i64, index.to_i64(), "{:?}", index);
assert_eq!(to_u32, index.to_u32(), "{:?}", index);
assert_eq!(to_u64, index.to_u64(), "{:?}", index);
}
}
#[test]
fn index_tag() {
let index = Index::new(1).with_tag(&tags::VALUE_OFFSET);
assert_eq!(Some(&tags::VALUE_OFFSET), index.tag());
}
#[test]
fn index_eq() {
let a = Index::new(1);
let b = Index::new_u32(1);
let c = Index::new(2);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn tag_eq() {
let a = Tag::new("a");
let b = Tag::new("a");
let c = Tag::new("b");
assert_eq!(a.id, b.id);
assert_ne!(a.id, c.id);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn tag_match() {
const A: Tag = Tag::new("a");
let a = Tag::new("a");
match a {
A => (),
_ => panic!("unexpected tag `{:?}`", a),
}
}
#[cfg(feature = "alloc")]
mod alloc_support {
use crate::data::*;
use crate::std::string::String;
#[test]
fn label_owned() {
let label = Label::new_owned(String::from("a"));
assert!(label.as_static_str().is_none());
assert_eq!("a", label.as_str());
}
#[test]
fn label_owned_clone() {
let a = Label::new_owned(String::from("a"));
let b = a.clone();
assert_ne!(
a.backing_field_owned.as_ref().unwrap(),
b.backing_field_owned.as_ref().unwrap(),
);
}
}
}