@@ -18,8 +18,15 @@ use serde::{Deserialize, Serialize};
1818/// [`crate::msa::MSA`] can be parameterised over any alphabet without
1919/// duplicating logic.
2020pub trait AlphabetEncoding {
21+ /// Encoded symbol type. Implementors must be `#[repr(u8)]` (size 1, align 1)
22+ /// so that a `&[Self::Symbol]` can be reinterpreted as `&[u8]` by
23+ /// [`as_bytes`](AlphabetEncoding::as_bytes) for the byte-level distance kernel.
2124 type Symbol : Copy ;
2225
26+ /// Byte value of the gap symbol (`Self::Symbol::Gap as u8`). Used by the
27+ /// branchless / SIMD distance kernel for gap detection without enum matching.
28+ const GAP_BYTE : u8 ;
29+
2330 /// Encodes a byte into the corresponding symbol in the alphabet.
2431 fn encode ( symbol : u8 ) -> Self :: Symbol ;
2532
@@ -28,6 +35,18 @@ pub trait AlphabetEncoding {
2835
2936 /// Returns `true` if `symbol` represents a gap (`-`).
3037 fn is_gap ( symbol : Self :: Symbol ) -> bool ;
38+
39+ /// Reinterprets an encoded sequence as a raw byte slice.
40+ ///
41+ /// The default implementation relies on the trait invariant that
42+ /// `Self::Symbol` is `#[repr(u8)]`, so a slice of symbols has the exact same
43+ /// layout as a `&[u8]` of equal length. All built-in alphabets uphold this.
44+ fn as_bytes ( seq : & [ Self :: Symbol ] ) -> & [ u8 ] {
45+ debug_assert_eq ! ( core:: mem:: size_of:: <Self :: Symbol >( ) , 1 ) ;
46+ // SAFETY: `Self::Symbol` is `#[repr(u8)]` (1 byte, align 1), so the slice
47+ // has identical layout to `&[u8]` of the same length.
48+ unsafe { core:: slice:: from_raw_parts ( seq. as_ptr ( ) as * const u8 , seq. len ( ) ) }
49+ }
3150}
3251
3352/// A single nucleotide in the DNA alphabet.
@@ -36,13 +55,14 @@ pub trait AlphabetEncoding {
3655/// character in aligned sequences. Any byte not matching `A/C/G/T/N/-`
3756/// (case-insensitive) is mapped to `N`.
3857#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
58+ #[ repr( u8 ) ]
3959pub enum DnaSymbol {
40- A ,
41- C ,
42- G ,
43- T ,
44- N ,
45- Gap ,
60+ A = 0 ,
61+ C = 1 ,
62+ G = 2 ,
63+ T = 3 ,
64+ N = 4 ,
65+ Gap = 5 ,
4666}
4767
4868/// Marker struct for the DNA alphabet.
@@ -54,6 +74,8 @@ pub struct DNA;
5474impl AlphabetEncoding for DNA {
5575 type Symbol = DnaSymbol ;
5676
77+ const GAP_BYTE : u8 = DnaSymbol :: Gap as u8 ;
78+
5779 fn encode ( symbol : u8 ) -> Self :: Symbol {
5880 match symbol {
5981 b'A' | b'a' => DnaSymbol :: A ,
@@ -88,29 +110,30 @@ impl AlphabetEncoding for DNA {
88110/// (the `-` alignment character). Encoding is case-insensitive; any byte that
89111/// does not match a known amino acid letter is mapped to `X`.
90112#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
113+ #[ repr( u8 ) ]
91114pub enum ProteinSymbol {
92- A ,
93- R ,
94- N ,
95- D ,
96- C ,
97- Q ,
98- E ,
99- G ,
100- H ,
101- I ,
102- L ,
103- K ,
104- M ,
105- F ,
106- P ,
107- S ,
108- T ,
109- W ,
110- Y ,
111- V ,
112- X ,
113- Gap ,
115+ A = 0 ,
116+ R = 1 ,
117+ N = 2 ,
118+ D = 3 ,
119+ C = 4 ,
120+ Q = 5 ,
121+ E = 6 ,
122+ G = 7 ,
123+ H = 8 ,
124+ I = 9 ,
125+ L = 10 ,
126+ K = 11 ,
127+ M = 12 ,
128+ F = 13 ,
129+ P = 14 ,
130+ S = 15 ,
131+ T = 16 ,
132+ W = 17 ,
133+ Y = 18 ,
134+ V = 19 ,
135+ X = 20 ,
136+ Gap = 21 ,
114137}
115138
116139/// Marker struct for the protein alphabet.
@@ -122,6 +145,8 @@ pub struct Protein;
122145impl AlphabetEncoding for Protein {
123146 type Symbol = ProteinSymbol ;
124147
148+ const GAP_BYTE : u8 = ProteinSymbol :: Gap as u8 ;
149+
125150 fn encode ( symbol : u8 ) -> Self :: Symbol {
126151 // Match on the uppercased byte so lowercase residues encode identically
127152 // (consistent with `DNA::encode` and `detect_alphabet`, which both
0 commit comments