async_snmp/
oid.rs

1//! Object Identifier (OID) type.
2//!
3//! OIDs are stored as `SmallVec<[u32; 16]>` to avoid heap allocation for common OIDs.
4
5use crate::error::{DecodeErrorKind, Error, OidErrorKind, Result};
6use smallvec::SmallVec;
7use std::fmt;
8
9/// Maximum number of arcs (subidentifiers) allowed in an OID.
10///
11/// Net-snmp limits OIDs to 128 subidentifiers (`MAX_OID_LEN` in `types.h`).
12/// This provides protection against DoS attacks from maliciously long OIDs.
13///
14/// This limit is enforced optionally via [`Oid::validate_length()`]. The standard
15/// [`Oid::from_ber()`] and [`Oid::parse()`] methods accept OIDs of any length for
16/// maximum flexibility.
17pub const MAX_OID_LEN: usize = 128;
18
19/// Object Identifier.
20///
21/// Stored as a sequence of arc values (u32). Uses SmallVec to avoid
22/// heap allocation for OIDs with 16 or fewer arcs.
23#[derive(Clone, PartialEq, Eq, Hash)]
24pub struct Oid {
25    arcs: SmallVec<[u32; 16]>,
26}
27
28impl Oid {
29    /// Create an empty OID.
30    pub fn empty() -> Self {
31        Self {
32            arcs: SmallVec::new(),
33        }
34    }
35
36    /// Create an OID from arc values.
37    pub fn new(arcs: impl IntoIterator<Item = u32>) -> Self {
38        Self {
39            arcs: arcs.into_iter().collect(),
40        }
41    }
42
43    /// Create an OID from a slice of arcs.
44    pub fn from_slice(arcs: &[u32]) -> Self {
45        Self {
46            arcs: SmallVec::from_slice(arcs),
47        }
48    }
49
50    /// Parse an OID from dotted string notation (e.g., "1.3.6.1.2.1.1.1.0").
51    ///
52    /// # Validation
53    ///
54    /// This method parses the string format but does **not** validate arc constraints
55    /// per X.690 Section 8.19.4. Invalid OIDs like `"3.0"` (arc1 must be 0, 1, or 2)
56    /// or `"0.40"` (arc2 must be ≤39 when arc1 < 2) will parse successfully.
57    ///
58    /// To validate arc constraints, call [`validate()`](Self::validate) after parsing,
59    /// or use [`to_ber_checked()`](Self::to_ber_checked) which validates before encoding.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use async_snmp::oid::Oid;
65    ///
66    /// // Valid OID
67    /// let oid = Oid::parse("1.3.6.1.2.1.1.1.0").unwrap();
68    /// assert!(oid.validate().is_ok());
69    ///
70    /// // Invalid arc1 parses but fails validation
71    /// let invalid = Oid::parse("3.0").unwrap();
72    /// assert!(invalid.validate().is_err());
73    /// ```
74    pub fn parse(s: &str) -> Result<Self> {
75        if s.is_empty() {
76            return Ok(Self::empty());
77        }
78
79        let mut arcs = SmallVec::new();
80
81        for part in s.split('.') {
82            if part.is_empty() {
83                continue;
84            }
85
86            let arc: u32 = part.parse().map_err(|_| {
87                Error::invalid_oid_with_input(OidErrorKind::InvalidArc, s.to_string())
88            })?;
89
90            arcs.push(arc);
91        }
92
93        Ok(Self { arcs })
94    }
95
96    /// Get the arc values.
97    pub fn arcs(&self) -> &[u32] {
98        &self.arcs
99    }
100
101    /// Get the number of arcs.
102    pub fn len(&self) -> usize {
103        self.arcs.len()
104    }
105
106    /// Check if the OID is empty.
107    pub fn is_empty(&self) -> bool {
108        self.arcs.is_empty()
109    }
110
111    /// Check if this OID starts with another OID.
112    pub fn starts_with(&self, other: &Oid) -> bool {
113        self.arcs.len() >= other.arcs.len() && self.arcs[..other.arcs.len()] == other.arcs[..]
114    }
115
116    /// Get the parent OID (all arcs except the last).
117    pub fn parent(&self) -> Option<Oid> {
118        if self.arcs.is_empty() {
119            None
120        } else {
121            Some(Oid {
122                arcs: SmallVec::from_slice(&self.arcs[..self.arcs.len() - 1]),
123            })
124        }
125    }
126
127    /// Create a child OID by appending an arc.
128    pub fn child(&self, arc: u32) -> Oid {
129        let mut arcs = self.arcs.clone();
130        arcs.push(arc);
131        Oid { arcs }
132    }
133
134    /// Validate OID arcs per X.690 Section 8.19.4.
135    ///
136    /// - arc1 must be 0, 1, or 2
137    /// - arc2 must be <= 39 when arc1 is 0 or 1
138    /// - arc2 can be any value when arc1 is 2
139    pub fn validate(&self) -> Result<()> {
140        if self.arcs.is_empty() {
141            return Ok(());
142        }
143
144        let arc1 = self.arcs[0];
145
146        // arc1 must be 0, 1, or 2
147        if arc1 > 2 {
148            return Err(Error::invalid_oid(OidErrorKind::InvalidFirstArc(arc1)));
149        }
150
151        // arc2 must be <= 39 when arc1 < 2
152        if self.arcs.len() >= 2 {
153            let arc2 = self.arcs[1];
154            if arc1 < 2 && arc2 >= 40 {
155                return Err(Error::invalid_oid(OidErrorKind::InvalidSecondArc {
156                    first: arc1,
157                    second: arc2,
158                }));
159            }
160        }
161
162        Ok(())
163    }
164
165    /// Validate that the OID doesn't exceed the maximum arc count.
166    ///
167    /// Net-snmp limits OIDs to 128 subidentifiers (`MAX_OID_LEN`). This check
168    /// provides protection against DoS attacks from maliciously long OIDs.
169    ///
170    /// # Examples
171    ///
172    /// ```
173    /// use async_snmp::oid::{Oid, MAX_OID_LEN};
174    ///
175    /// let oid = Oid::parse("1.3.6.1.2.1.1.1.0").unwrap();
176    /// assert!(oid.validate_length().is_ok());
177    ///
178    /// // Create an OID with too many arcs
179    /// let too_long: Vec<u32> = (0..150).collect();
180    /// let long_oid = Oid::new(too_long);
181    /// assert!(long_oid.validate_length().is_err());
182    /// ```
183    pub fn validate_length(&self) -> Result<()> {
184        if self.arcs.len() > MAX_OID_LEN {
185            return Err(Error::invalid_oid(OidErrorKind::TooManyArcs {
186                count: self.arcs.len(),
187                max: MAX_OID_LEN,
188            }));
189        }
190        Ok(())
191    }
192
193    /// Validate both arc constraints and length.
194    ///
195    /// Combines [`validate()`](Self::validate) and [`validate_length()`](Self::validate_length).
196    pub fn validate_all(&self) -> Result<()> {
197        self.validate()?;
198        self.validate_length()
199    }
200
201    /// Encode to BER format, returning bytes in a stack-allocated buffer.
202    ///
203    /// Uses SmallVec to avoid heap allocation for OIDs with up to ~20 arcs.
204    /// This is the optimized version used internally by encoding routines.
205    ///
206    /// OID encoding (X.690 Section 8.19):
207    /// - First two arcs encoded as (arc1 * 40) + arc2 using base-128
208    /// - Remaining arcs encoded as base-128 variable length
209    pub fn to_ber_smallvec(&self) -> SmallVec<[u8; 64]> {
210        let mut bytes = SmallVec::new();
211
212        if self.arcs.is_empty() {
213            return bytes;
214        }
215
216        // First two arcs combined into first subidentifier
217        // Uses base-128 encoding because arc2 can be > 127 when arc1=2
218        if self.arcs.len() >= 2 {
219            let first_subid = self.arcs[0] * 40 + self.arcs[1];
220            encode_subidentifier_smallvec(&mut bytes, first_subid);
221        } else if self.arcs.len() == 1 {
222            let first_subid = self.arcs[0] * 40;
223            encode_subidentifier_smallvec(&mut bytes, first_subid);
224        }
225
226        // Remaining arcs (only if there are more than 2)
227        if self.arcs.len() > 2 {
228            for &arc in &self.arcs[2..] {
229                encode_subidentifier_smallvec(&mut bytes, arc);
230            }
231        }
232
233        bytes
234    }
235
236    /// Encode to BER format.
237    ///
238    /// OID encoding (X.690 Section 8.19):
239    /// - First two arcs encoded as (arc1 * 40) + arc2 using base-128
240    /// - Remaining arcs encoded as base-128 variable length
241    ///
242    /// # Empty OID Encoding
243    ///
244    /// Empty OIDs are encoded as zero bytes (empty content). Note that net-snmp
245    /// encodes empty OIDs as `[0x00]` (single zero byte). This difference is
246    /// unlikely to matter in practice since empty OIDs are rarely used in SNMP.
247    ///
248    /// # Validation
249    ///
250    /// This method does not validate arc constraints. Use [`to_ber_checked()`](Self::to_ber_checked)
251    /// for validation, or call [`validate()`](Self::validate) first.
252    pub fn to_ber(&self) -> Vec<u8> {
253        self.to_ber_smallvec().to_vec()
254    }
255
256    /// Encode to BER format with validation.
257    ///
258    /// Returns an error if the OID has invalid arcs per X.690 Section 8.19.4.
259    pub fn to_ber_checked(&self) -> Result<Vec<u8>> {
260        self.validate()?;
261        Ok(self.to_ber())
262    }
263
264    /// Decode from BER format.
265    pub fn from_ber(data: &[u8]) -> Result<Self> {
266        if data.is_empty() {
267            return Ok(Self::empty());
268        }
269
270        let mut arcs = SmallVec::new();
271
272        // Decode first subidentifier (which encodes arc1*40 + arc2)
273        // This may be multi-byte for large arc2 values (when arc1=2)
274        let (first_subid, consumed) = decode_subidentifier(data)?;
275
276        // Decode first two arcs from the first subidentifier
277        if first_subid < 40 {
278            arcs.push(0);
279            arcs.push(first_subid);
280        } else if first_subid < 80 {
281            arcs.push(1);
282            arcs.push(first_subid - 40);
283        } else {
284            arcs.push(2);
285            arcs.push(first_subid - 80);
286        }
287
288        // Decode remaining arcs
289        let mut i = consumed;
290        while i < data.len() {
291            let (arc, bytes_consumed) = decode_subidentifier(&data[i..])?;
292            arcs.push(arc);
293            i += bytes_consumed;
294        }
295
296        Ok(Self { arcs })
297    }
298}
299
300/// Encode a subidentifier in base-128 variable length into a SmallVec.
301#[inline]
302fn encode_subidentifier_smallvec(bytes: &mut SmallVec<[u8; 64]>, value: u32) {
303    if value == 0 {
304        bytes.push(0);
305        return;
306    }
307
308    // Count how many 7-bit groups we need
309    let mut temp = value;
310    let mut count = 0;
311    while temp > 0 {
312        count += 1;
313        temp >>= 7;
314    }
315
316    // Encode from MSB to LSB
317    for i in (0..count).rev() {
318        let mut byte = ((value >> (i * 7)) & 0x7F) as u8;
319        if i > 0 {
320            byte |= 0x80; // Continuation bit
321        }
322        bytes.push(byte);
323    }
324}
325
326/// Decode a subidentifier, returning (value, bytes_consumed).
327fn decode_subidentifier(data: &[u8]) -> Result<(u32, usize)> {
328    let mut value: u32 = 0;
329    let mut i = 0;
330
331    loop {
332        if i >= data.len() {
333            return Err(Error::decode(i, DecodeErrorKind::TruncatedData));
334        }
335
336        let byte = data[i];
337        i += 1;
338
339        // Check for overflow before shifting
340        if value > (u32::MAX >> 7) {
341            return Err(Error::decode(i, DecodeErrorKind::IntegerOverflow));
342        }
343
344        value = (value << 7) | ((byte & 0x7F) as u32);
345
346        if byte & 0x80 == 0 {
347            // Last byte
348            break;
349        }
350    }
351
352    Ok((value, i))
353}
354
355impl fmt::Debug for Oid {
356    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357        write!(f, "Oid({})", self)
358    }
359}
360
361impl fmt::Display for Oid {
362    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363        let mut first = true;
364        for arc in &self.arcs {
365            if !first {
366                write!(f, ".")?;
367            }
368            write!(f, "{}", arc)?;
369            first = false;
370        }
371        Ok(())
372    }
373}
374
375impl std::str::FromStr for Oid {
376    type Err = crate::error::Error;
377
378    fn from_str(s: &str) -> Result<Self> {
379        Self::parse(s)
380    }
381}
382
383impl From<&[u32]> for Oid {
384    fn from(arcs: &[u32]) -> Self {
385        Self::from_slice(arcs)
386    }
387}
388
389impl<const N: usize> From<[u32; N]> for Oid {
390    fn from(arcs: [u32; N]) -> Self {
391        Self::new(arcs)
392    }
393}
394
395impl PartialOrd for Oid {
396    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
397        Some(self.cmp(other))
398    }
399}
400
401impl Ord for Oid {
402    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
403        self.arcs.cmp(&other.arcs)
404    }
405}
406
407/// Macro to create an OID at compile time.
408///
409/// # Examples
410///
411/// ```
412/// use async_snmp::oid;
413///
414/// let sys_descr = oid!(1, 3, 6, 1, 2, 1, 1, 1, 0);
415/// ```
416#[macro_export]
417macro_rules! oid {
418    ($($arc:expr),* $(,)?) => {
419        $crate::oid::Oid::from_slice(&[$($arc),*])
420    };
421}
422
423#[cfg(test)]
424mod tests {
425    use super::*;
426
427    #[test]
428    fn test_parse() {
429        let oid = Oid::parse("1.3.6.1.2.1.1.1.0").unwrap();
430        assert_eq!(oid.arcs(), &[1, 3, 6, 1, 2, 1, 1, 1, 0]);
431    }
432
433    #[test]
434    fn test_display() {
435        let oid = Oid::from_slice(&[1, 3, 6, 1, 2, 1, 1, 1, 0]);
436        assert_eq!(oid.to_string(), "1.3.6.1.2.1.1.1.0");
437    }
438
439    #[test]
440    fn test_starts_with() {
441        let oid = Oid::parse("1.3.6.1.2.1.1.1.0").unwrap();
442        let prefix = Oid::parse("1.3.6.1").unwrap();
443        assert!(oid.starts_with(&prefix));
444        assert!(!prefix.starts_with(&oid));
445    }
446
447    #[test]
448    fn test_ber_roundtrip() {
449        let oid = Oid::parse("1.3.6.1.2.1.1.1.0").unwrap();
450        let ber = oid.to_ber();
451        let decoded = Oid::from_ber(&ber).unwrap();
452        assert_eq!(oid, decoded);
453    }
454
455    #[test]
456    fn test_ber_encoding() {
457        // 1.3.6.1 encodes as: (1*40+3)=43, 6, 1 = [0x2B, 0x06, 0x01]
458        let oid = Oid::parse("1.3.6.1").unwrap();
459        assert_eq!(oid.to_ber(), vec![0x2B, 0x06, 0x01]);
460    }
461
462    #[test]
463    fn test_macro() {
464        let oid = oid!(1, 3, 6, 1);
465        assert_eq!(oid.arcs(), &[1, 3, 6, 1]);
466    }
467
468    // AUDIT-001: Test arc validation
469    // X.690 Section 8.19.4: arc1 must be 0, 1, or 2; arc2 must be <= 39 when arc1 < 2
470    #[test]
471    fn test_validate_arc1_must_be_0_1_or_2() {
472        // arc1 = 3 is invalid
473        let oid = Oid::from_slice(&[3, 0]);
474        let result = oid.validate();
475        assert!(result.is_err(), "arc1=3 should be invalid");
476    }
477
478    #[test]
479    fn test_validate_arc2_limit_when_arc1_is_0() {
480        // arc1 = 0, arc2 = 40 is invalid (max is 39)
481        let oid = Oid::from_slice(&[0, 40]);
482        let result = oid.validate();
483        assert!(result.is_err(), "arc2=40 with arc1=0 should be invalid");
484
485        // arc1 = 0, arc2 = 39 is valid
486        let oid = Oid::from_slice(&[0, 39]);
487        assert!(
488            oid.validate().is_ok(),
489            "arc2=39 with arc1=0 should be valid"
490        );
491    }
492
493    #[test]
494    fn test_validate_arc2_limit_when_arc1_is_1() {
495        // arc1 = 1, arc2 = 40 is invalid
496        let oid = Oid::from_slice(&[1, 40]);
497        let result = oid.validate();
498        assert!(result.is_err(), "arc2=40 with arc1=1 should be invalid");
499
500        // arc1 = 1, arc2 = 39 is valid
501        let oid = Oid::from_slice(&[1, 39]);
502        assert!(
503            oid.validate().is_ok(),
504            "arc2=39 with arc1=1 should be valid"
505        );
506    }
507
508    #[test]
509    fn test_validate_arc2_no_limit_when_arc1_is_2() {
510        // arc1 = 2, arc2 can be anything (e.g., 999)
511        let oid = Oid::from_slice(&[2, 999]);
512        assert!(
513            oid.validate().is_ok(),
514            "arc2=999 with arc1=2 should be valid"
515        );
516    }
517
518    #[test]
519    fn test_to_ber_validates_arcs() {
520        // Invalid OID should return error from to_ber_checked
521        let oid = Oid::from_slice(&[3, 0]); // arc1=3 is invalid
522        let result = oid.to_ber_checked();
523        assert!(
524            result.is_err(),
525            "to_ber_checked should fail for invalid arc1"
526        );
527    }
528
529    // AUDIT-002: Test first subidentifier encoding for large arc2 values
530    // X.690 Section 8.19 example: OID {2 999 3} has first subidentifier = 1079
531    #[test]
532    fn test_ber_encoding_large_arc2() {
533        // OID 2.999.3: first subid = 2*40 + 999 = 1079 = 0x437
534        // 1079 in base-128: 0x88 0x37 (continuation bit set on first byte)
535        let oid = Oid::from_slice(&[2, 999, 3]);
536        let ber = oid.to_ber();
537        // First subidentifier 1079 = 0b10000110111 = 7 bits: 0b0110111 (0x37), 7 bits: 0b0001000 (0x08)
538        // In base-128: (1079 >> 7) = 8, (1079 & 0x7F) = 55
539        // So: 0x88 (8 | 0x80), 0x37 (55)
540        assert_eq!(
541            ber[0], 0x88,
542            "first byte should be 0x88 (8 with continuation)"
543        );
544        assert_eq!(
545            ber[1], 0x37,
546            "second byte should be 0x37 (55, no continuation)"
547        );
548        assert_eq!(ber[2], 0x03, "third byte should be 0x03 (arc 3)");
549        assert_eq!(ber.len(), 3, "OID 2.999.3 should encode to 3 bytes");
550    }
551
552    #[test]
553    fn test_ber_roundtrip_large_arc2() {
554        // Ensure roundtrip works for OID with large arc2
555        let oid = Oid::from_slice(&[2, 999, 3]);
556        let ber = oid.to_ber();
557        let decoded = Oid::from_ber(&ber).unwrap();
558        assert_eq!(oid, decoded, "roundtrip should preserve OID 2.999.3");
559    }
560
561    #[test]
562    fn test_ber_encoding_arc2_equals_80() {
563        // Edge case: arc1=2, arc2=0 gives first subid = 80, which is exactly 1 byte
564        let oid = Oid::from_slice(&[2, 0]);
565        let ber = oid.to_ber();
566        assert_eq!(ber, vec![80], "OID 2.0 should encode to [80]");
567    }
568
569    #[test]
570    fn test_ber_encoding_arc2_equals_127() {
571        // arc1=2, arc2=47 gives first subid = 127, still fits in 1 byte
572        let oid = Oid::from_slice(&[2, 47]);
573        let ber = oid.to_ber();
574        assert_eq!(ber, vec![127], "OID 2.47 should encode to [127]");
575    }
576
577    #[test]
578    fn test_ber_encoding_arc2_equals_128_needs_2_bytes() {
579        // arc1=2, arc2=48 gives first subid = 128, needs 2 bytes in base-128
580        let oid = Oid::from_slice(&[2, 48]);
581        let ber = oid.to_ber();
582        // 128 = 0x80 = base-128: 0x81 0x00
583        assert_eq!(
584            ber,
585            vec![0x81, 0x00],
586            "OID 2.48 should encode to [0x81, 0x00]"
587        );
588    }
589
590    #[test]
591    fn test_oid_non_minimal_subidentifier() {
592        // Non-minimal subidentifier encoding with leading 0x80 bytes should be accepted
593        // 0x80 0x01 should decode as 1 (non-minimal: minimal would be just 0x01)
594        // OID: 1.3 followed by arc 1 encoded as 0x80 0x01
595        let result = Oid::from_ber(&[0x2B, 0x80, 0x01]);
596        assert!(
597            result.is_ok(),
598            "should accept non-minimal subidentifier 0x80 0x01"
599        );
600        let oid = result.unwrap();
601        assert_eq!(oid.arcs(), &[1, 3, 1]);
602
603        // 0x80 0x80 0x01 should decode as 1 (two leading 0x80 bytes)
604        let result = Oid::from_ber(&[0x2B, 0x80, 0x80, 0x01]);
605        assert!(
606            result.is_ok(),
607            "should accept non-minimal subidentifier 0x80 0x80 0x01"
608        );
609        let oid = result.unwrap();
610        assert_eq!(oid.arcs(), &[1, 3, 1]);
611
612        // 0x80 0x00 should decode as 0 (non-minimal zero)
613        let result = Oid::from_ber(&[0x2B, 0x80, 0x00]);
614        assert!(
615            result.is_ok(),
616            "should accept non-minimal subidentifier 0x80 0x00"
617        );
618        let oid = result.unwrap();
619        assert_eq!(oid.arcs(), &[1, 3, 0]);
620    }
621
622    // Tests for MAX_OID_LEN validation
623    #[test]
624    fn test_validate_length_within_limit() {
625        // OID with MAX_OID_LEN arcs should be valid
626        let arcs: Vec<u32> = (0..MAX_OID_LEN as u32).collect();
627        let oid = Oid::new(arcs);
628        assert!(
629            oid.validate_length().is_ok(),
630            "OID with exactly MAX_OID_LEN arcs should be valid"
631        );
632    }
633
634    #[test]
635    fn test_validate_length_exceeds_limit() {
636        // OID with more than MAX_OID_LEN arcs should fail
637        let arcs: Vec<u32> = (0..(MAX_OID_LEN + 1) as u32).collect();
638        let oid = Oid::new(arcs);
639        let result = oid.validate_length();
640        assert!(
641            result.is_err(),
642            "OID exceeding MAX_OID_LEN should fail validation"
643        );
644    }
645
646    #[test]
647    fn test_validate_all_combines_checks() {
648        // Valid OID
649        let oid = Oid::from_slice(&[1, 3, 6, 1]);
650        assert!(oid.validate_all().is_ok());
651
652        // Invalid arc1 (fails validate)
653        let oid = Oid::from_slice(&[3, 0]);
654        assert!(oid.validate_all().is_err());
655
656        // Too many arcs (fails validate_length)
657        let arcs: Vec<u32> = (0..(MAX_OID_LEN + 1) as u32).collect();
658        let oid = Oid::new(arcs);
659        assert!(oid.validate_all().is_err());
660    }
661
662    #[test]
663    fn test_oid_fromstr() {
664        // Test basic parsing via FromStr trait
665        let oid: Oid = "1.3.6.1.2.1.1.1.0".parse().unwrap();
666        assert_eq!(oid, oid!(1, 3, 6, 1, 2, 1, 1, 1, 0));
667
668        // Test empty OID
669        let empty: Oid = "".parse().unwrap();
670        assert!(empty.is_empty());
671
672        // Test single arc
673        let single: Oid = "1".parse().unwrap();
674        assert_eq!(single.arcs(), &[1]);
675
676        // Test roundtrip Display -> FromStr
677        let original = oid!(1, 3, 6, 1, 4, 1, 9, 9, 42);
678        let displayed = original.to_string();
679        let parsed: Oid = displayed.parse().unwrap();
680        assert_eq!(original, parsed);
681    }
682
683    #[test]
684    fn test_oid_fromstr_invalid() {
685        // Invalid arc value
686        assert!("1.3.abc.1".parse::<Oid>().is_err());
687
688        // Negative number (parsed as invalid)
689        assert!("1.3.-6.1".parse::<Oid>().is_err());
690    }
691}