noodles_bgzf/
virtual_position.rs1use std::{error, fmt};
4
5pub(crate) const MAX_COMPRESSED_POSITION: u64 = (1 << 48) - 1;
6pub(crate) const MAX_UNCOMPRESSED_POSITION: u16 = u16::MAX;
7
8const COMPRESSED_POSITION_SHIFT: u64 = 16;
9const UNCOMPRESSED_POSITION_MASK: u64 = 0xffff;
10
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
34pub struct VirtualPosition(u64);
35
36impl VirtualPosition {
37 pub const MIN: Self = Self(u64::MIN);
39
40 pub const MAX: Self = Self(u64::MAX);
42
43 pub const fn new(compressed_pos: u64, uncompressed_pos: u16) -> Option<Self> {
53 if compressed_pos <= MAX_COMPRESSED_POSITION {
54 let virtual_pos =
56 (compressed_pos << COMPRESSED_POSITION_SHIFT) | uncompressed_pos as u64;
57
58 Some(Self(virtual_pos))
59 } else {
60 None
61 }
62 }
63
64 pub const fn compressed(self) -> u64 {
78 self.0 >> COMPRESSED_POSITION_SHIFT
79 }
80
81 pub const fn uncompressed(self) -> u16 {
93 (self.0 & UNCOMPRESSED_POSITION_MASK) as u16
94 }
95}
96
97impl From<u64> for VirtualPosition {
98 fn from(pos: u64) -> Self {
99 Self(pos)
100 }
101}
102
103#[derive(Clone, Debug, Eq, PartialEq)]
105pub enum TryFromU64U16TupleError {
106 CompressedPositionOverflow,
108}
109
110impl error::Error for TryFromU64U16TupleError {}
111
112impl fmt::Display for TryFromU64U16TupleError {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self {
115 Self::CompressedPositionOverflow => {
116 f.write_str("the compressed position is larger than 2^48 - 1")
117 }
118 }
119 }
120}
121
122impl TryFrom<(u64, u16)> for VirtualPosition {
123 type Error = TryFromU64U16TupleError;
124
125 fn try_from(pos: (u64, u16)) -> Result<Self, Self::Error> {
135 let (compressed_pos, uncompressed_pos) = pos;
136
137 if compressed_pos > MAX_COMPRESSED_POSITION {
138 return Err(TryFromU64U16TupleError::CompressedPositionOverflow);
139 }
140
141 Ok(Self(
142 (compressed_pos << COMPRESSED_POSITION_SHIFT) | u64::from(uncompressed_pos),
143 ))
144 }
145}
146
147impl From<VirtualPosition> for u64 {
148 fn from(pos: VirtualPosition) -> Self {
149 pos.0
150 }
151}
152
153impl From<VirtualPosition> for (u64, u16) {
154 fn from(pos: VirtualPosition) -> Self {
155 (pos.compressed(), pos.uncompressed())
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 #[test]
164 fn test_from_u64_for_virtual_position() {
165 let pos = VirtualPosition::from(88384945211);
166 assert_eq!(pos.compressed(), 1348647);
167 assert_eq!(pos.uncompressed(), 15419);
168
169 let pos = VirtualPosition::from(188049630896);
170 assert_eq!(pos.compressed(), 2869409);
171 assert_eq!(pos.uncompressed(), 42672);
172
173 let pos = VirtualPosition::from(26155658182977);
174 assert_eq!(pos.compressed(), 399103671);
175 assert_eq!(pos.uncompressed(), 321);
176 }
177
178 #[test]
179 fn test_try_from_u64_u16_tuple_for_virtual_position() {
180 assert_eq!(
181 VirtualPosition::try_from((1348647, 15419)),
182 Ok(VirtualPosition::from(88384945211))
183 );
184
185 assert_eq!(
186 VirtualPosition::try_from((2869409, 42672)),
187 Ok(VirtualPosition::from(188049630896))
188 );
189
190 assert_eq!(
191 VirtualPosition::try_from((399103671, 321)),
192 Ok(VirtualPosition::from(26155658182977))
193 );
194
195 assert_eq!(
196 VirtualPosition::try_from((281474976710656, 0)),
197 Err(TryFromU64U16TupleError::CompressedPositionOverflow)
198 );
199 }
200
201 #[test]
202 fn test_from_virtual_position_for_u64() {
203 assert_eq!(u64::from(VirtualPosition::from(88384945211)), 88384945211);
204 assert_eq!(u64::from(VirtualPosition::from(188049630896)), 188049630896);
205 assert_eq!(
206 u64::from(VirtualPosition::from(26155658182977)),
207 26155658182977
208 );
209 }
210
211 #[test]
212 fn test_from_virtual_position_for_u64_u16_tuple() {
213 assert_eq!(
214 <(u64, u16)>::from(VirtualPosition::from(88384945211)),
215 (1348647, 15419)
216 );
217
218 assert_eq!(
219 <(u64, u16)>::from(VirtualPosition::from(188049630896)),
220 (2869409, 42672)
221 );
222
223 assert_eq!(
224 <(u64, u16)>::from(VirtualPosition::from(26155658182977)),
225 (399103671, 321)
226 );
227 }
228}