1use crate::{Integer, cons::Cons};
2use core::fmt::Debug;
3
4pub trait Value: Clone + Copy + Default + PartialEq + Eq + PartialOrd + Ord {
6 type Number: Integer;
8
9 type Pointer: Integer;
11
12 fn from_number(number: Self::Number) -> Self;
14
15 fn to_number(self) -> Self::Number;
17
18 fn from_pointer(cons: Self::Pointer) -> Self;
20
21 fn to_pointer(self) -> Self::Pointer;
23
24 fn is_pointer(self) -> bool;
26
27 fn mark(self, mark: bool) -> Self;
29
30 fn is_marked(self) -> bool;
32
33 fn to_cons(self) -> Cons<Self> {
35 Cons::new(self)
36 }
37
38 fn from_cons(cons: Cons<Self>) -> Self {
40 cons.to_value()
41 }
42}
43
44#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
46pub struct Value16(u16);
47
48#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
50pub struct Value32(u32);
51
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
54pub struct Value64(u64);
55
56macro_rules! impl_value {
57 ($value:ty, $number:ty, $pointer:ty) => {
58 impl Value for $value {
59 type Number = $number;
60 type Pointer = $pointer;
61
62 #[inline]
63 fn from_number(number: Self::Number) -> Self {
64 Self(((number << 2) | 1) as _)
65 }
66
67 #[inline]
68 fn to_number(self) -> Self::Number {
69 self.0 as Self::Number >> 2
70 }
71
72 #[inline]
73 fn from_pointer(pointer: Self::Pointer) -> Self {
74 Self(pointer << 2)
75 }
76
77 #[inline]
78 fn to_pointer(self) -> Self::Pointer {
79 self.0 >> 2
80 }
81
82 #[inline]
83 fn is_pointer(self) -> bool {
84 self.0 & 1 == 0
85 }
86
87 #[inline]
88 fn mark(self, mark: bool) -> Self {
89 Self(if mark { self.0 | 0b10 } else { self.0 & !0b10 })
90 }
91
92 #[inline]
93 fn is_marked(self) -> bool {
94 self.0 & 0b10 != 0
95 }
96 }
97 };
98}
99
100impl_value!(Value16, i16, u16);
101impl_value!(Value32, i32, u32);
102impl_value!(Value64, i64, u64);
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 macro_rules! test_value {
109 ($name:ident, $value:ty) => {
110 mod $name {
111 use super::*;
112 use core::mem::size_of;
113
114 fn from_number(number: <$value as Value>::Number) -> $value {
115 <$value as Value>::from_number(number)
116 }
117
118 fn from_pointer(pointer: <$value as Value>::Pointer) -> $value {
119 <$value as Value>::from_pointer(pointer)
120 }
121
122 #[test]
123 fn size() {
124 assert_eq!(
125 size_of::<<$value as Value>::Number>(),
126 size_of::<<$value as Value>::Pointer>()
127 );
128 }
129
130 #[test]
131 fn convert_number() {
132 assert_eq!(from_number(-42).to_number(), -42);
133 assert_eq!(from_number(-1).to_number(), -1);
134 assert_eq!(from_number(0).to_number(), 0);
135 assert_eq!(from_number(1).to_number(), 1);
136 assert_eq!(from_number(42).to_number(), 42);
137 }
138
139 #[test]
140 fn convert_pointer() {
141 assert_eq!(from_pointer(0).to_pointer(), 0);
142 assert_eq!(from_pointer(1).to_pointer(), 1);
143 assert_eq!(from_pointer(42).to_pointer(), 42);
144 }
145
146 #[test]
147 fn check_pointer() {
148 assert!(from_pointer(0).is_pointer());
149 assert!(!from_number(0).is_pointer());
150 }
151
152 #[test]
153 fn is_marked() {
154 assert!(!from_pointer(0).is_marked());
155 assert!(!from_number(0).is_marked());
156 }
157
158 #[test]
159 fn mark_number() {
160 assert!(!from_number(0).mark(false).is_marked());
161 assert!(from_number(0).mark(true).is_marked());
162 assert_eq!(from_number(0).mark(false).to_number(), 0);
163 assert_eq!(from_number(42).mark(false).to_number(), 42);
164 assert_eq!(from_number(-42).mark(false).to_number(), -42);
165 assert_eq!(from_number(0).mark(true).to_number(), 0);
166 assert_eq!(from_number(42).mark(true).to_number(), 42);
167 assert_eq!(from_number(-42).mark(true).to_number(), -42);
168 }
169
170 #[test]
171 fn mark_pointer() {
172 assert!(!from_pointer(0).mark(false).is_marked());
173 assert!(from_pointer(0).mark(true).is_marked());
174
175 assert_eq!(from_pointer(0).mark(false).to_pointer(), 0);
176 assert_eq!(from_pointer(42).mark(false).to_pointer(), 42);
177 assert_eq!(from_pointer(0).mark(true).to_pointer(), 0);
178 assert_eq!(from_pointer(42).mark(true).to_pointer(), 42);
179 }
180 }
181 };
182 }
183
184 test_value!(value16, Value16);
185 test_value!(value32, Value32);
186 test_value!(value64, Value64);
187}