1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! Encodes an EVR (epoch, version, release) as a memcmp-sortable binary key.
//! The returned `Vec<u8>` produces correct RPM version ordering when compared
//! byte-by-byte (memcmp-style), matching the semantics of `rpmvercmp()`.
//!
//! Unlike the `Evr` struct which uses a traditional approach to
//!
//! The key is structured as: [4-byte epoch] [version sortkey] [release sortkey]
//!
//! Epoch is encoded as a 4-byte big-endian unsigned integer. Because memcmp
//! compares left-to-right, the epoch bytes are always compared first, and a
//! higher epoch always dominates regardless of version/release — matching RPM
//! semantics. NULL epochs are coalesced to 0.
//!
//! Only ASCII alphanumeric characters participate in comparison - non-ASCII
//! characters and non-alphanumeric ASCII characters (except ~ and ^) are
//! treated as segment separators and skipped (matching RPM).
//!
//! Each version/release sortkey uses six byte markers, chosen so their numeric
//! order matches the required RPM sort order:
//!
//! \x00 — segment delimiter (terminates alpha and numeric segments)
//! \x01 — tilde (~), sorts lowest, before end-of-version
//! \x02 — end-of-version (appended once at the end of the key)
//! \x03 — caret (^), sorts after end but before real segments
//! \x04 — alpha segment prefix (followed by the segment text + \x00 delimiter)
//! \x05 — numeric segment prefix (followed by length byte + digits + \x00)
//!
//! This order directly encodes the RPM rule: ~ < end < ^ < alpha < numeric.
//!
//! Segment encoding details:
//!
//! Alpha segments: \x04 + raw ASCII text + \x00
//! Lexicographic byte comparison gives correct alphabetical ordering.
//!
//! Numeric segments: \x05 + length_byte + stripped_digits + \x00
//! Leading zeros are stripped. The length byte sorts first, so longer digit
//! strings (= larger numbers) always sort after shorter ones. Within the
//! same length, digit characters compare lexicographically, which is correct
//! for same-length decimal integers.
//!
//! Each version/release component ends with \x02 (end-of-version). When two
//! versions are equal, the comparison naturally falls through the end marker
//! into the release bytes. This is why no separator is needed between the
//! version and release sortkeys — the \x02 end marker serves as a delimiter.
//!
//! Example: EVR "2:1.0~rc1-3.fc40" encodes as:
//!
//! epoch (4-byte big-endian):
//! \x00 \x00 \x00 \x02 — epoch 2
//!
//! version sortkey ("1.0~rc1"):
//! \x05 \x01 1 \x00 — numeric segment "1"
//! \x05 \x00 \x00 — numeric segment "0" (leading zeros stripped, length=0)
//! \x01 — tilde
//! \x04 rc \x00 — alpha segment "rc"
//! \x05 \x01 1 \x00 — numeric segment "1"
//! \x02 — end-of-version
//!
//! release sortkey ("3.fc40"):
//! \x05 \x01 3 \x00 — numeric segment "3"
//! \x04 fc \x00 — alpha segment "fc"
//! \x05 \x02 40 \x00 — numeric segment "40"
//! \x02 — end-of-version
//!
//! Comparing "1.0~rc1" vs "1.0": after the shared prefix "1.0", the first key
//! has \x01 (tilde) while the second has \x02 (end). Since \x01 < \x02,
//! "1.0~rc1" correctly sorts before "1.0".
use crateEvr;
/// Encode an EVR (epoch, version, release) as a memcmp-sortable binary key.
///
/// The returned `Vec<u8>` produces correct RPM version ordering when compared
/// byte-by-byte, matching the semantics of `rpmvercmp()`.
///
/// This property is extremely useful, because it means that the value can be stored
/// in a database and used to perform in-database RPM version comparisons, provided
/// the database is capable of memcmp-style order comparisons across collections
/// of raw bytes.
///
/// The key layout is: `[4-byte big-endian epoch] [version sortkey] [release sortkey]`
///
/// Empty or unparseable epochs are treated as 0, matching RPM semantics.
;
/// Encode a version or release string as a memcmp-sortable binary key.
///
/// The returned `Vec<u8>` can be compared with standard byte comparison
/// (`memcmp` / `Ord for [u8]`) to produce correct RPM version ordering.
///
/// See [`evr_sortkey`] for the full EVR encoding.