pub mod fucid;
pub mod rngid;
pub mod ufoid;
use std::borrow::Borrow;
use std::cell::RefCell;
use std::convert::TryInto;
use std::fmt::Display;
use std::fmt::LowerHex;
use std::fmt::UpperHex;
use std::hash::Hash;
use std::marker::PhantomData;
use std::mem;
use std::num::NonZero;
use std::ops::Deref;
use hex::FromHex;
pub use fucid::fucid;
pub use fucid::FUCIDsource;
pub use rngid::rngid as genid;
pub use rngid::rngid;
pub use ufoid::ufoid;
use crate::patch::Entry;
use crate::patch::IdentitySchema;
use crate::patch::PATCH;
use crate::prelude::valueschemas::GenId;
use crate::query::Constraint;
use crate::query::ContainsConstraint;
use crate::query::Variable;
use crate::value::RawValue;
use crate::value::VALUE_LEN;
thread_local!(static OWNED_IDS: IdOwner = IdOwner::new());
pub const ID_LEN: usize = 16;
pub type RawId = [u8; ID_LEN];
pub(crate) fn id_into_value(id: &RawId) -> RawValue {
let mut data = [0; VALUE_LEN];
data[16..32].copy_from_slice(id);
data
}
pub(crate) fn id_from_value(id: &RawValue) -> Option<RawId> {
if id[0..16] != [0; 16] {
return None;
}
let id = id[16..32].try_into().unwrap();
Some(id)
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(C, packed(1))]
pub struct Id {
inner: NonZero<u128>,
}
impl Id {
pub const fn new(id: RawId) -> Option<Self> {
unsafe { std::mem::transmute::<RawId, Option<Id>>(id) }
}
pub fn from_hex(hex: &str) -> Option<Self> {
let raw = <RawId as FromHex>::from_hex(hex).ok()?;
Id::new(raw)
}
pub const unsafe fn force(id: RawId) -> Self {
std::mem::transmute::<RawId, Id>(id)
}
pub fn as_transmute_raw(id: &RawId) -> Option<&Self> {
if *id == [0; 16] {
None
} else {
Some(unsafe { std::mem::transmute::<&RawId, &Id>(id) })
}
}
pub fn acquire(&self) -> Option<ExclusiveId> {
OWNED_IDS.with(|owner| owner.take(self))
}
pub const fn raw(self) -> RawId {
unsafe { std::mem::transmute::<Id, RawId>(self) }
}
}
impl PartialOrd for Id {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Id {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let s: &RawId = self;
let o: &RawId = other;
Ord::cmp(s, o)
}
}
impl Hash for Id {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let s: &RawId = self;
Hash::hash(s, state);
}
}
impl Deref for Id {
type Target = RawId;
fn deref(&self) -> &Self::Target {
unsafe { std::mem::transmute::<&Id, &RawId>(self) }
}
}
impl Borrow<RawId> for Id {
fn borrow(&self) -> &RawId {
self
}
}
impl AsRef<RawId> for Id {
fn as_ref(&self) -> &RawId {
self
}
}
impl AsRef<[u8]> for Id {
fn as_ref(&self) -> &[u8] {
&self[..]
}
}
impl From<Id> for RawId {
fn from(id: Id) -> Self {
*id
}
}
impl From<Id> for RawValue {
fn from(id: Id) -> Self {
let raw: RawId = id.into();
id_into_value(&raw)
}
}
impl Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Id({self:X})")
}
}
impl LowerHex for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for byte in &self[..] {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
impl UpperHex for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for byte in &self[..] {
write!(f, "{byte:02X}")?;
}
Ok(())
}
}
impl From<Id> for uuid::Uuid {
fn from(id: Id) -> Self {
let id: &RawId = &id;
uuid::Uuid::from_slice(id).unwrap()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NilUuidError;
impl TryFrom<uuid::Uuid> for Id {
type Error = NilUuidError;
fn try_from(id: uuid::Uuid) -> Result<Self, NilUuidError> {
let bytes = id.into_bytes();
Id::new(bytes).ok_or(NilUuidError)
}
}
impl std::fmt::Display for NilUuidError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"UUID conversion failed: the UUID is nil (all zero bytes)"
)
}
}
impl std::error::Error for NilUuidError {}
#[doc(hidden)]
pub use hex_literal::hex as _hex_literal_hex;
#[macro_export]
macro_rules! id_hex {
( $data:expr ) => {
$crate::id::Id::new($crate::id::_hex_literal_hex!($data)).unwrap()
};
}
pub use id_hex;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ExclusiveId {
pub id: Id,
_private: PhantomData<*const ()>,
}
unsafe impl Send for ExclusiveId {}
impl ExclusiveId {
pub fn force(id: Id) -> Self {
Self {
id,
_private: PhantomData,
}
}
pub fn force_ref(id: &Id) -> &Self {
unsafe { std::mem::transmute(id) }
}
pub fn release(self) -> Id {
let id = self.id;
mem::drop(self);
id
}
pub fn forget(self) -> Id {
let id = self.id;
mem::forget(self);
id
}
}
impl Drop for ExclusiveId {
fn drop(&mut self) {
OWNED_IDS.with(|ids| {
ids.force_insert(self);
});
}
}
impl Deref for ExclusiveId {
type Target = Id;
fn deref(&self) -> &Self::Target {
&self.id
}
}
impl Borrow<RawId> for ExclusiveId {
fn borrow(&self) -> &RawId {
self
}
}
impl Borrow<Id> for ExclusiveId {
fn borrow(&self) -> &Id {
self
}
}
impl AsRef<Id> for ExclusiveId {
fn as_ref(&self) -> &Id {
self
}
}
impl AsRef<ExclusiveId> for ExclusiveId {
fn as_ref(&self) -> &ExclusiveId {
self
}
}
impl AsRef<RawId> for ExclusiveId {
fn as_ref(&self) -> &RawId {
self
}
}
impl AsRef<[u8]> for ExclusiveId {
fn as_ref(&self) -> &[u8] {
&self[..]
}
}
impl Display for ExclusiveId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let id: &Id = self;
write!(f, "ExclusiveId({id:X})")
}
}
pub fn local_ids(v: Variable<GenId>) -> impl Constraint<'static> {
OWNED_IDS.with(|owner| owner.has(v))
}
pub struct IdOwner {
owned_ids: RefCell<PATCH<ID_LEN, IdentitySchema, ()>>,
}
pub struct OwnedId<'a> {
pub id: Id,
owner: &'a IdOwner,
}
impl Default for IdOwner {
fn default() -> Self {
Self::new()
}
}
impl IdOwner {
pub fn new() -> Self {
Self {
owned_ids: RefCell::new(PATCH::<ID_LEN, IdentitySchema, ()>::new()),
}
}
pub fn insert(&mut self, id: ExclusiveId) -> Id {
self.force_insert(&id);
id.forget()
}
pub fn defer_insert(&self, id: ExclusiveId) -> OwnedId<'_> {
OwnedId {
id: id.forget(),
owner: self,
}
}
pub fn force_insert(&self, id: &Id) {
let entry = Entry::new(id);
self.owned_ids.borrow_mut().insert(&entry);
}
pub fn take(&self, id: &Id) -> Option<ExclusiveId> {
if self.owned_ids.borrow().has_prefix(id) {
self.owned_ids.borrow_mut().remove(id);
Some(ExclusiveId::force(*id))
} else {
None
}
}
pub fn borrow<'a>(&'a self, id: &Id) -> Option<OwnedId<'a>> {
self.take(id).map(move |id| OwnedId {
id: id.forget(),
owner: self,
})
}
pub fn owns(&self, id: &Id) -> bool {
self.owned_ids.borrow().has_prefix(id)
}
}
impl Deref for OwnedId<'_> {
type Target = ExclusiveId;
fn deref(&self) -> &Self::Target {
ExclusiveId::force_ref(&self.id)
}
}
impl Borrow<RawId> for OwnedId<'_> {
fn borrow(&self) -> &RawId {
self
}
}
impl Borrow<Id> for OwnedId<'_> {
fn borrow(&self) -> &Id {
self
}
}
impl Borrow<ExclusiveId> for OwnedId<'_> {
fn borrow(&self) -> &ExclusiveId {
self
}
}
impl AsRef<ExclusiveId> for OwnedId<'_> {
fn as_ref(&self) -> &ExclusiveId {
self
}
}
impl AsRef<Id> for OwnedId<'_> {
fn as_ref(&self) -> &Id {
self
}
}
impl AsRef<RawId> for OwnedId<'_> {
fn as_ref(&self) -> &RawId {
self
}
}
impl AsRef<[u8]> for OwnedId<'_> {
fn as_ref(&self) -> &[u8] {
&self[..]
}
}
impl Display for OwnedId<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let id: &Id = self;
write!(f, "OwnedId({id:X})")
}
}
impl<'a> Drop for OwnedId<'a> {
fn drop(&mut self) {
self.owner.force_insert(&(self.id));
}
}
impl ContainsConstraint<'static, GenId> for &IdOwner {
type Constraint =
<PATCH<ID_LEN, IdentitySchema, ()> as ContainsConstraint<'static, GenId>>::Constraint;
fn has(self, v: Variable<GenId>) -> Self::Constraint {
self.owned_ids.borrow().clone().has(v)
}
}
#[cfg(test)]
mod tests {
use crate::examples::literature;
use crate::id::ExclusiveId;
use crate::prelude::*;
use crate::query::Query;
use crate::query::VariableContext;
use crate::value::schemas::genid::GenId;
use crate::value::schemas::shortstring::ShortString;
#[test]
fn id_formatting() {
let id: Id = id_hex!("7D06820D69947D76E7177E5DEA4EA773");
assert_eq!(format!("{id:x}"), "7d06820d69947d76e7177e5dea4ea773");
assert_eq!(format!("{id:X}"), "7D06820D69947D76E7177E5DEA4EA773");
}
#[test]
fn ns_local_ids() {
let mut kb = TribleSet::new();
{
let isaac = ufoid();
let jules = ufoid();
kb += entity! { &jules @
literature::firstname: "Jules",
literature::lastname: "Verne"
};
kb += entity! { &isaac @
literature::firstname: "Isaac",
literature::lastname: "Asimov"
};
}
let mut r: Vec<_> = find!(
(author: ExclusiveId, name: String),
and!(
local_ids(author),
pattern!(&kb, [
{?author @
literature::firstname: ?name
}])
)
)
.map(|(_, n)| n)
.collect();
r.sort();
assert_eq!(vec!["Isaac", "Jules"], r);
}
#[test]
fn ns_local_ids_bad_estimates_panics() {
let mut kb = TribleSet::new();
{
let isaac = ufoid();
let jules = ufoid();
kb += entity! { &jules @
literature::firstname: "Jules",
literature::lastname: "Verne"
};
kb += entity! { &isaac @
literature::firstname: "Isaac",
literature::lastname: "Asimov"
};
}
let mut ctx = VariableContext::new();
macro_rules! __local_find_context {
() => {
&mut ctx
};
}
let author = ctx.next_variable::<GenId>();
let name = ctx.next_variable::<ShortString>();
let base = and!(
local_ids(author),
pattern!(&kb, [{ ?author @ literature::firstname: ?name }])
);
let mut wrapper = crate::debug::query::EstimateOverrideConstraint::new(base);
wrapper.set_estimate(author.index, 100);
wrapper.set_estimate(name.index, 1);
let q: Query<_, _, _> = Query::new(wrapper, |binding| {
Some(name.extract(binding).try_from_value::<String>().unwrap())
});
let r: Vec<_> = q.collect();
assert_eq!(r, vec!["Isaac", "Jules"]);
}
}