1 module asn1.types.oidtype;
2 import asn1.codec : ASN1ValueException;
3 import std.ascii : isGraphical;
4 import std.exception : assertThrown;
5 
6 ///
7 public alias OIDNode = ObjectIdentifierNode;
8 /**
9     A struct representing a single node in an OID, which has a mandatory
10     number and an optional descriptor.
11 */
12 public
13 struct ObjectIdentifierNode
14 {
15     /**
16         The unique unsigned integral number associated with a node in the
17         object identifier hierarchy.
18     */
19     public size_t number;
20     private string _descriptor;
21 
22     /**
23         The descriptor string is an ObjectDescriptor, which is defined as:
24 
25         $(MONO ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
26 
27         GraphicString is just $(D 0x20) to $(D 0x7E), therefore
28         ObjectDescriptor is just $(D 0x20) to $(D 0x7E).
29 
30         It is used to describe the object identified by this node.
31     */
32     public @property @safe pure nothrow
33     string descriptor() const
34     {
35         return this._descriptor.dup;
36     }
37 
38     /**
39         The descriptor string is an ObjectDescriptor, which is defined as:
40 
41         $(MONO ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
42 
43         GraphicString is just $(D 0x20) to $(D 0x7E), therefore
44         ObjectDescriptor is just $(D 0x20) to $(D 0x7E).
45 
46         It is used to describe the object identified by this node.
47     */
48     public @property @safe pure
49     void descriptor(string value)
50     {
51         scope (success) this._descriptor = value;
52         foreach (immutable character; value)
53         {
54             if ((!character.isGraphical) && (character != ' '))
55             {
56                 throw new ASN1ValueException
57                 (
58                     "This exception was thrown because you tried to decode " ~
59                     "a GraphicString that contained a character that " ~
60                     "is not graphical (a character whose ASCII encoding " ~
61                     "is outside of the range 0x20 to 0x7E). The offending " ~
62                     "character is '" ~ character ~ "'. " ~
63                     "For more information on Object Identifiers, read the " ~
64                     "International Telecommunications Union's X.660 specification, " ~
65                     "which can be found at " ~
66                     "http://www.itu.int/rec/T-REC-X.660-201107-I/en. " ~
67                     "If you believe that you have " ~
68                     "discovered a bug, please create an issue on the GitHub page's Issues " ~
69                     "section at: https://github.com/JonathanWilbur/asn1-d/issues. "
70                 );
71             }
72         }
73     }
74 
75     /// Override for use of the `==` operand.
76     public @safe @nogc nothrow pure
77     bool opEquals(const OIDNode other) const
78     {
79         return (this.number == other.number);
80     }
81 
82     ///
83     @system
84     unittest
85     {
86         immutable OIDNode a = OIDNode(1, "iso");
87         immutable OIDNode b = OIDNode(1, "not-iso");
88         assert(a == b);
89     }
90 
91     /// Override for the use of the '>', '<', '<=', and '>=' operands.
92     public @safe @nogc nothrow pure
93     ptrdiff_t opCmp(ref in OIDNode other) const
94     {
95         return cast(ptrdiff_t) (this.number - other.number);
96     }
97 
98     ///
99     @system
100     unittest
101     {
102         immutable OIDNode a = OIDNode(1, "iso");
103         immutable OIDNode b = OIDNode(2, "even-more-iso");
104         assert(b > a);
105     }
106 
107     /**
108         An override so that associative arrays can use an $(D OIDNode) as a
109         key.
110         Returns: A $(D size_t) that represents a hash of the $(D OIDNode)
111     */
112     public @trusted nothrow
113     size_t toHash() const
114     {
115         return typeid(this.number).getHash(cast(const void*) &this.number);
116     }
117 
118     /**
119         A constructor that accepts a descriptor string.
120         The descriptor string is an ObjectDescriptor, which is defined as:
121 
122         $(MONO ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
123 
124         $(MONO GraphicString) is just a string containing only characters between
125         and including $(D 0x20) and $(D 0x7E), therefore ObjectDescriptor is just
126         $(D 0x20) and $(D 0x7E).
127 
128         Throws:
129         $(UL
130             $(LI $(D ASN1ValueException) if the encoded value contains any bytes
131                 outside of $(D 0x20) to $(D 0x7E))
132         )
133     */
134     public @safe pure
135     this(in size_t number, in string descriptor = "")
136     {
137         this.number = number;
138         this.descriptor = descriptor;
139     }
140 
141     @system
142     unittest
143     {
144         immutable OIDNode a = OIDNode(1, "Nitro dubs & T-Rix");
145         assert(a.descriptor == "Nitro dubs & T-Rix");
146         immutable OIDNode b = OIDNode(1, " ");
147         assert(b.descriptor == " ");
148         immutable OIDNode c = OIDNode(1, "");
149         assert(c.descriptor == "");
150         assertThrown!ASN1ValueException(OIDNode(1, "\xD7"));
151         assertThrown!ASN1ValueException(OIDNode(1, "\t"));
152         assertThrown!ASN1ValueException(OIDNode(1, "\r"));
153         assertThrown!ASN1ValueException(OIDNode(1, "\n"));
154         assertThrown!ASN1ValueException(OIDNode(1, "\b"));
155         assertThrown!ASN1ValueException(OIDNode(1, "\v"));
156         assertThrown!ASN1ValueException(OIDNode(1, "\f"));
157         assertThrown!ASN1ValueException(OIDNode(1, "\0"));
158     }
159 }