1 module snmp.oid; 2 3 import c.net_snmp; 4 5 private alias size_t = object.size_t; 6 7 /// Fixed-capacity OID with inline storage. 8 struct OID 9 { 10 oid[MAX_OID_LEN] data = 0; 11 size_t length = 0; 12 13 /// Returns invalid OID (length == 0) on failure. 14 static OID fromString(scope const(char)* str) @trusted @nogc nothrow 15 { 16 OID o; 17 o.length = MAX_OID_LEN; 18 if (!snmp_parse_oid(str, o.data.ptr, &o.length)) 19 o.length = 0; 20 return o; 21 } 22 23 static OID fromString(string s) @trusted nothrow 24 { 25 import std.string : toStringz; 26 27 return fromString(s.toStringz); 28 } 29 30 /// Truncates silently to MAX_OID_LEN. 31 static OID fromSlice(scope const(oid)[] src) @trusted @nogc nothrow 32 { 33 import core.stdc.string : memcpy; 34 35 OID o; 36 o.length = src.length < MAX_OID_LEN ? src.length : MAX_OID_LEN; 37 memcpy(o.data.ptr, src.ptr, o.length * oid.sizeof); 38 return o; 39 } 40 41 bool isValid() const @nogc nothrow @safe 42 { 43 return length > 0; 44 } 45 46 inout(oid)* ptr() inout @trusted @nogc nothrow 47 { 48 return data.ptr; 49 } 50 51 /// Returns 0 for out-of-bounds indices. 52 oid opIndex(size_t i) const @nogc nothrow @safe 53 { 54 return i < length ? data[i] : 0; 55 } 56 57 int toString(scope char[] buf) const @trusted @nogc nothrow 58 { 59 if (!isValid || buf.length == 0) 60 return 0; 61 return snprint_objid(buf.ptr, buf.length, data.ptr, length); 62 } 63 64 string toString() const @trusted nothrow 65 { 66 char[512] buf = void; 67 int n = toString(buf[]); 68 return n > 0 ? buf[0 .. n].idup : "(invalid)"; 69 } 70 71 bool opEquals(scope const OID rhs) const @trusted @nogc nothrow 72 { 73 if (length != rhs.length) 74 return false; 75 import core.stdc.string : memcmp; 76 77 return memcmp(data.ptr, rhs.data.ptr, length * oid.sizeof) == 0; 78 } 79 80 int opCmp(scope const OID rhs) const @trusted @nogc nothrow 81 { 82 import core.stdc.string : memcmp; 83 84 size_t common = length < rhs.length ? length : rhs.length; 85 if (common) 86 { 87 int c = memcmp(data.ptr, rhs.data.ptr, common * oid.sizeof); 88 if (c != 0) return c; 89 } 90 return length < rhs.length ? -1 : length > rhs.length ? 1 : 0; 91 } 92 93 /// True when this OID is a proper prefix of `other`. 94 bool isPrefixOf(scope const OID other) const @trusted @nogc nothrow 95 { 96 if (!isValid || length >= other.length) 97 return false; 98 import core.stdc.string : memcmp; 99 100 return memcmp(data.ptr, other.data.ptr, length * oid.sizeof) == 0; 101 } 102 } 103 104 unittest 105 { 106 init_snmp("snmp-d-test"); 107 108 auto o = OID.fromString("1.3.6.1.2.1.1.1.0"); 109 assert(o.isValid); 110 assert(o.length == 9); 111 assert(o.data[0] == 1); 112 assert(o.data[1] == 3); 113 assert(o.data[2] == 6); 114 115 assert(o[0] == 1); 116 assert(o[2] == 6); 117 assert(o[100] == 0); 118 119 auto o2 = OID.fromString("1.3.6.1.2.1.1.1.0"); 120 assert(o == o2); 121 122 auto prefix = OID.fromString("1.3.6.1.2.1.1"); 123 assert(prefix < o); 124 assert(o > prefix); 125 126 auto sliced = OID.fromSlice(o.data[0 .. o.length]); 127 assert(sliced == o); 128 129 assert(prefix.isValid); 130 assert(prefix.isPrefixOf(o)); 131 assert(!o.isPrefixOf(prefix)); 132 133 OID empty; 134 assert(!empty.isValid); 135 assert(!empty.isPrefixOf(o)); 136 }