Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions rust/rubydex/src/model/declaration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,13 +503,13 @@ impl Namespace {
}

namespace_declaration!(Class, ClassDeclaration);
assert_mem_size!(ClassDeclaration, 224);
assert_mem_size!(ClassDeclaration, 216);
namespace_declaration!(Module, ModuleDeclaration);
assert_mem_size!(ModuleDeclaration, 224);
assert_mem_size!(ModuleDeclaration, 216);
namespace_declaration!(SingletonClass, SingletonClassDeclaration);
assert_mem_size!(SingletonClassDeclaration, 224);
assert_mem_size!(SingletonClassDeclaration, 216);
namespace_declaration!(Todo, TodoDeclaration);
assert_mem_size!(TodoDeclaration, 224);
assert_mem_size!(TodoDeclaration, 216);
simple_declaration!(ConstantDeclaration);
assert_mem_size!(ConstantDeclaration, 112);
simple_declaration!(MethodDeclaration);
Expand Down
28 changes: 14 additions & 14 deletions rust/rubydex/src/model/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ pub struct ClassDefinition {
superclass_ref: Option<ReferenceId>,
mixins: Vec<Mixin>,
}
assert_mem_size!(ClassDefinition, 144);
assert_mem_size!(ClassDefinition, 128);

impl ClassDefinition {
#[must_use]
Expand Down Expand Up @@ -369,7 +369,7 @@ pub struct SingletonClassDefinition {
/// Mixins declared in this singleton class
mixins: Vec<Mixin>,
}
assert_mem_size!(SingletonClassDefinition, 128);
assert_mem_size!(SingletonClassDefinition, 120);

impl SingletonClassDefinition {
#[must_use]
Expand Down Expand Up @@ -473,7 +473,7 @@ pub struct ModuleDefinition {
members: Vec<DefinitionId>,
mixins: Vec<Mixin>,
}
assert_mem_size!(ModuleDefinition, 128);
assert_mem_size!(ModuleDefinition, 120);

impl ModuleDefinition {
#[must_use]
Expand Down Expand Up @@ -573,7 +573,7 @@ pub struct ConstantDefinition {
comments: Vec<Comment>,
lexical_nesting_id: Option<DefinitionId>,
}
assert_mem_size!(ConstantDefinition, 72);
assert_mem_size!(ConstantDefinition, 64);

impl ConstantDefinition {
#[must_use]
Expand Down Expand Up @@ -643,7 +643,7 @@ pub struct ConstantAliasDefinition {
alias_constant: ConstantDefinition,
target_name_id: NameId,
}
assert_mem_size!(ConstantAliasDefinition, 80);
assert_mem_size!(ConstantAliasDefinition, 72);

impl ConstantAliasDefinition {
#[must_use]
Expand Down Expand Up @@ -721,7 +721,7 @@ pub struct MethodDefinition {
receiver: Option<Receiver>,
}

assert_mem_size!(MethodDefinition, 112);
assert_mem_size!(MethodDefinition, 104);

/// The receiver of a singleton method definition.
#[derive(Debug)]
Expand Down Expand Up @@ -877,7 +877,7 @@ pub struct AttrAccessorDefinition {
lexical_nesting_id: Option<DefinitionId>,
visibility: Visibility,
}
assert_mem_size!(AttrAccessorDefinition, 72);
assert_mem_size!(AttrAccessorDefinition, 64);

impl AttrAccessorDefinition {
#[must_use]
Expand Down Expand Up @@ -958,7 +958,7 @@ pub struct AttrReaderDefinition {
lexical_nesting_id: Option<DefinitionId>,
visibility: Visibility,
}
assert_mem_size!(AttrReaderDefinition, 72);
assert_mem_size!(AttrReaderDefinition, 64);

impl AttrReaderDefinition {
#[must_use]
Expand Down Expand Up @@ -1039,7 +1039,7 @@ pub struct AttrWriterDefinition {
lexical_nesting_id: Option<DefinitionId>,
visibility: Visibility,
}
assert_mem_size!(AttrWriterDefinition, 72);
assert_mem_size!(AttrWriterDefinition, 64);

impl AttrWriterDefinition {
#[must_use]
Expand Down Expand Up @@ -1119,7 +1119,7 @@ pub struct GlobalVariableDefinition {
comments: Vec<Comment>,
lexical_nesting_id: Option<DefinitionId>,
}
assert_mem_size!(GlobalVariableDefinition, 72);
assert_mem_size!(GlobalVariableDefinition, 64);

impl GlobalVariableDefinition {
#[must_use]
Expand Down Expand Up @@ -1192,7 +1192,7 @@ pub struct InstanceVariableDefinition {
comments: Vec<Comment>,
lexical_nesting_id: Option<DefinitionId>,
}
assert_mem_size!(InstanceVariableDefinition, 72);
assert_mem_size!(InstanceVariableDefinition, 64);

impl InstanceVariableDefinition {
#[must_use]
Expand Down Expand Up @@ -1265,7 +1265,7 @@ pub struct ClassVariableDefinition {
comments: Vec<Comment>,
lexical_nesting_id: Option<DefinitionId>,
}
assert_mem_size!(ClassVariableDefinition, 72);
assert_mem_size!(ClassVariableDefinition, 64);

impl ClassVariableDefinition {
#[must_use]
Expand Down Expand Up @@ -1333,7 +1333,7 @@ pub struct MethodAliasDefinition {
comments: Vec<Comment>,
lexical_nesting_id: Option<DefinitionId>,
}
assert_mem_size!(MethodAliasDefinition, 80);
assert_mem_size!(MethodAliasDefinition, 72);

impl MethodAliasDefinition {
#[must_use]
Expand Down Expand Up @@ -1414,7 +1414,7 @@ pub struct GlobalVariableAliasDefinition {
comments: Vec<Comment>,
lexical_nesting_id: Option<DefinitionId>,
}
assert_mem_size!(GlobalVariableAliasDefinition, 80);
assert_mem_size!(GlobalVariableAliasDefinition, 72);

impl GlobalVariableAliasDefinition {
#[must_use]
Expand Down
59 changes: 53 additions & 6 deletions rust/rubydex/src/model/id.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,43 @@
use std::{
hash::{Hash, Hasher},
marker::PhantomData,
num::NonZeroU64,
ops::Deref,
};
use xxhash_rust::xxh3;

/// A deterministic type-safe ID representation
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// Maps a u64 hash to a `NonZeroU64` by replacing 0 with `u64::MAX`.
/// The probability of a 64-bit hash being exactly 0 is 2^-64 (~5.4e-20),
/// and remapping 0 → MAX just means those two inputs collide — the same
/// risk as any other hash collision, which the system already handles.
#[inline]
fn to_non_zero(value: u64) -> NonZeroU64 {
NonZeroU64::new(value).unwrap_or(NonZeroU64::new(u64::MAX).unwrap())
}

/// A deterministic type-safe ID representation.
///
/// Uses `NonZeroU64` internally so that `Option<Id<T>>` is 8 bytes (same as `Id<T>`)
/// via niche optimization, instead of 16 bytes with a plain `u64`.
#[derive(Debug, Clone, Copy)]
pub struct Id<T> {
value: u64,
value: NonZeroU64,
_marker: PhantomData<T>,
}

impl<T> PartialEq for Id<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}

impl<T> Eq for Id<T> {}

impl<T> Id<T> {
#[must_use]
pub fn new(value: u64) -> Self {
Self {
value,
value: to_non_zero(value),
_marker: PhantomData,
}
}
Expand All @@ -26,7 +47,9 @@ impl<T> Deref for Id<T> {
type Target = u64;

fn deref(&self) -> &Self::Target {
&self.value
// SAFETY: NonZeroU64 has the same layout as u64, and we only need the numeric value.
// We return a reference to the inner u64 representation.
unsafe { &*(std::ptr::from_ref(&self.value).cast::<u64>()) }
}
}

Expand All @@ -36,9 +59,21 @@ impl<T> std::fmt::Display for Id<T> {
}
}

impl<T> PartialOrd for Id<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}

impl<T> Ord for Id<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.cmp(&other.value)
}
}

impl<T> Hash for Id<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.value);
state.write_u64(self.value.get());
}
}

Expand Down Expand Up @@ -81,4 +116,16 @@ mod tests {
let id = TestId::new(123);
assert_eq!(*id, 123);
}

#[test]
fn option_id_is_8_bytes() {
assert_eq!(std::mem::size_of::<Option<TestId>>(), 8);
assert_eq!(std::mem::size_of::<TestId>(), 8);
}

#[test]
fn zero_hash_maps_to_nonzero() {
let id = TestId::new(0);
assert_eq!(*id, u64::MAX);
}
}
2 changes: 1 addition & 1 deletion rust/rubydex/src/model/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ impl PartialEq for Name {
self.str == other.str && self.parent_scope == other.parent_scope && self.nesting == other.nesting
}
}
assert_mem_size!(Name, 48);
assert_mem_size!(Name, 40);

impl Name {
#[must_use]
Expand Down
2 changes: 1 addition & 1 deletion rust/rubydex/src/model/references.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub struct MethodRef {
/// The receiver of the method call if it's a constant
receiver: Option<NameId>,
}
assert_mem_size!(MethodRef, 40);
assert_mem_size!(MethodRef, 32);

impl MethodRef {
#[must_use]
Expand Down
Loading