1use super::Oid;
4use crate::error::{Error, ErrorCode, Path, Result};
5use crate::memory::{Memory, MemoryError, OwnedVec, Work};
6
7#[derive(Debug)]
13pub struct OwnedOid {
14 bytes: OwnedVec<u8>,
15}
16
17impl OwnedOid {
18 pub fn parse(text: &str, execution: &Memory) -> Result<Self> {
29 let work = (text.len() as u64)
30 .checked_mul(4)
31 .and_then(|work| work.checked_add(1))
32 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, 0))?;
33 execution
34 .charge_work(work)
35 .map_err(|reason| Error::resource(reason, 0))?;
36 let root = validate(text)?;
37 let mut bytes = execution.vector();
38 let mut groups = execution.vector();
39 let mut offset = 2;
40 for (position, component) in text.split('.').enumerate() {
41 if position == 0 {
42 continue;
43 }
44 groups.clear();
45 groups
46 .try_push(0u8)
47 .map_err(|reason| Error::resource(reason, offset))?;
48 for digit in component.bytes() {
49 multiply(&mut groups, digit - b'0', execution, offset)?;
50 }
51 if position == 1 {
52 add_root(&mut groups, root * 40, execution, offset)?;
53 }
54 execution
55 .charge_work(groups.len() as u64)
56 .map_err(|reason| Error::resource(reason, offset))?;
57 for at in (0..groups.len()).rev() {
58 let byte = if at == 0 {
59 groups[at]
60 } else {
61 groups[at] | 0x80
62 };
63 bytes
64 .try_push(byte)
65 .map_err(|reason| Error::resource(reason, offset))?;
66 }
67 offset += component.len() as u64 + 1;
68 }
69 Ok(Self { bytes })
70 }
71
72 pub fn from_oid(value: Oid<'_>, execution: &Memory) -> core::result::Result<Self, MemoryError> {
75 Self::from_oid_in(value, execution)
76 }
77
78 pub fn from_oid_in<W: Work>(
81 value: Oid<'_>,
82 execution: &W,
83 ) -> core::result::Result<Self, MemoryError> {
84 let mut bytes = execution.vector();
85 bytes.try_extend_from_slice(value.as_bytes())?;
86 Ok(Self {
87 bytes: bytes.into_owned(),
88 })
89 }
90
91 #[must_use]
93 pub fn as_oid(&self) -> Oid<'_> {
94 Oid(&self.bytes)
95 }
96}
97
98fn malformed(offset: usize) -> Error {
99 Error::new(ErrorCode::MalformedOid, offset as u64, Path::default())
100}
101
102fn validate(text: &str) -> Result<u8> {
103 let mut offset = 0;
107 let mut count = 0;
108 let mut root = 0;
109 for component in text.split('.') {
110 let bytes = component.as_bytes();
111 if bytes.is_empty() {
112 return Err(malformed(offset));
113 }
114 if bytes.len() > 1 && bytes[0] == b'0' {
115 return Err(malformed(offset));
116 }
117 for (at, byte) in bytes.iter().enumerate() {
118 if !byte.is_ascii_digit() {
119 return Err(malformed(offset + at));
120 }
121 }
122 if count == 0 {
123 if bytes.len() != 1 || bytes[0] > b'2' {
124 return Err(malformed(offset));
125 }
126 root = bytes[0] - b'0';
127 } else if count == 1
128 && root < 2
129 && (bytes.len() > 2 || (bytes.len() == 2 && bytes > &b"39"[..]))
130 {
131 return Err(malformed(offset));
132 }
133 count += 1;
134 offset += bytes.len() + 1;
135 }
136 if count < 2 {
137 return Err(malformed(text.len()));
138 }
139 Ok(root)
140}
141
142fn admit(groups: &OwnedVec<u8>, execution: &Memory, offset: u64) -> Result<()> {
143 let work = (groups.len() as u64)
144 .checked_mul(3)
145 .and_then(|work| work.checked_add(1))
146 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, offset))?;
147 execution
148 .charge_work(work)
149 .map_err(|reason| Error::resource(reason, offset))
150}
151
152fn multiply(groups: &mut OwnedVec<u8>, digit: u8, execution: &Memory, offset: u64) -> Result<()> {
153 admit(groups, execution, offset)?;
154 let mut carry = u16::from(digit);
155 for group in &mut **groups {
156 let current = u16::from(*group) * 10 + carry;
157 *group = u8::try_from(current & 0x7f).expect("seven-bit group");
158 carry = current >> 7;
159 }
160 while carry != 0 {
161 groups
162 .try_push(u8::try_from(carry & 0x7f).expect("seven-bit carry"))
163 .map_err(|reason| Error::resource(reason, offset))?;
164 carry >>= 7;
165 }
166 Ok(())
167}
168
169fn add_root(groups: &mut OwnedVec<u8>, root: u8, execution: &Memory, offset: u64) -> Result<()> {
170 admit(groups, execution, offset)?;
171 let mut carry = u16::from(root);
172 for group in &mut **groups {
173 if carry == 0 {
174 break;
175 }
176 let current = u16::from(*group) + carry;
177 *group = u8::try_from(current & 0x7f).expect("seven-bit group");
178 carry = current >> 7;
179 }
180 if carry != 0 {
181 groups
182 .try_push(u8::try_from(carry).expect("one-bit carry"))
183 .map_err(|reason| Error::resource(reason, offset))?;
184 }
185 Ok(())
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use crate::memory::MemoryAccount;
192 use alloc::sync::Arc;
193
194 fn memory(storage: usize, work: usize) -> Memory {
195 Memory::new(Arc::new(MemoryAccount::new(storage, work)))
196 }
197
198 #[test]
199 fn owned_decimal_oids_preserve_arbitrary_width_and_joint_root() {
200 for text in [
203 "0.0",
204 "1.39",
205 "2.0",
206 "2.18446744073709551616",
207 "1.2.10889035741470030830827987437816582766593",
208 ] {
209 let execution = memory(4096, 1_000_000);
210 let value = OwnedOid::parse(text, &execution).unwrap();
211 let rendered = value.as_oid().text(&execution).unwrap();
212 assert_eq!(rendered.as_str(&execution).unwrap(), text);
213 drop(rendered);
214 assert_eq!(execution.used(), value.bytes.capacity());
215 drop(value);
216 assert_eq!(execution.used(), 0);
217 }
218 }
219
220 #[test]
221 fn decimal_oid_syntax_refusal_has_no_retained_allocation() {
222 for (text, offset) in [
225 ("", 0),
226 ("1", 1),
227 ("1.", 2),
228 ("3.1", 0),
229 ("1.40", 2),
230 ("0.100", 2),
231 ("2.01", 2),
232 ("2.1..3", 4),
233 ("2.1x", 3),
234 (" 2.1", 0),
235 ] {
236 let execution = memory(0, 1024);
237 let error = OwnedOid::parse(text, &execution).unwrap_err();
238 assert_eq!(error.code(), ErrorCode::MalformedOid);
239 assert_eq!(error.offset(), offset);
240 assert_eq!(execution.used(), 0);
241 }
242 }
243
244 #[test]
245 fn owned_oid_refusal_releases_partial_conversion_and_copy_storage() {
246 let execution = memory(4096, 350);
249 let error =
250 OwnedOid::parse("2.1844674407370955161618446744073709551616", &execution).unwrap_err();
251 assert_eq!(error.code(), ErrorCode::LimitExceeded(crate::Limit::Work));
252 assert_eq!(execution.used(), 0);
253 let storage = memory(128, 10_000);
254 let original = OwnedOid::parse("1.2.840.113549", &storage).unwrap();
255 let used = storage.used();
256 let refused = memory(0, 1000);
257 assert_eq!(
258 OwnedOid::from_oid(original.as_oid(), &refused).unwrap_err(),
259 MemoryError::LimitExceeded
260 );
261 assert_eq!(refused.used(), 0);
262 assert_eq!(storage.used(), used);
263 assert_eq!(
264 original.as_oid(),
265 Oid::from_bytes(&[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d])
266 );
267 }
268}