1 /**
2     Abstract Syntax Notation 1 is a high-level syntax specification created
3     by the $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union) in
4     $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680 - Abstract Syntax Notation One (ASN.1)), that
5     abstractly defines data structures and protocol data units used by
6     programs and protocols. It defines an extensible system of data types,
7     modules, and data structures.
8 
9     While described abstractly by ASN.1, the specified protocol data units
10     and data structures can be encoded via various encoding schemes, such as
11     the Basic Encoding Rules (BER), which are defined in the
12     $(LINK https://www.itu.int/en/pages/default.aspx, International Telecommunications Union)'s
13     $(LINK http://www.itu.int/rec/T-REC-X.690/en, X.690 - ASN.1 encoding rules).
14     These encoding schemes uniformly relay data between systems that
15     can differ in endianness, bit-width, byte-size, operating system,
16     machine architecture, and so on.
17 
18     The encoding schemata that inherit from ASN.1 are used widely in protocols
19     such as TLS, LDAP, SNMP, RDP, and many more.
20 
21     This file contains all ASN.1 exceptions and the the ASN1Element class, which
22     is the abstract class from which all codecs in asn1/codecs/ will inherit.
23 
24     Authors:
25     $(UL
26         $(LI $(PERSON Jonathan M. Wilbur, jonathan@wilbur.space, http://jonathan.wilbur.space))
27     )
28     Copyright: Copyright (C) Jonathan M. Wilbur
29     License: $(LINK https://mit-license.org/, MIT License)
30     Standards:
31         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680 - Abstract Syntax Notation One (ASN.1))
32     See_Also:
33         $(LINK https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One, The Wikipedia Page on ASN.1)
34         $(LINK https://www.strozhevsky.com/free_docs/asn1_in_simple_words.pdf, ASN.1 By Simple Words)
35         $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF, ASN.1: Communication Between Heterogeneous Systems)
36 */
37 module asn1.codec;
38 public import asn1.constants;
39 public import asn1.types.alltypes;
40 public import asn1.types.identification;
41 public import asn1.types.oidtype;
42 public import std.algorithm.mutation : reverse;
43 public import std.algorithm.searching : canFind;
44 public import std.array : appender, Appender, replace, split;
45 public import std.ascii : isASCII, isGraphical;
46 public import std.bigint : BigInt;
47 public import std.conv : text, to;
48 public import std.datetime.date : DateTime;
49 public import std.datetime.systime : SysTime;
50 public import std.datetime.timezone : TimeZone, UTC;
51 private import std.exception : basicExceptionCtors;
52 public import std.math : isIdentical, isNaN, log2;
53 public import std.string : indexOf;
54 public import std.traits : isFloatingPoint, isIntegral, isSigned, isUnsigned;
55 
56 ///
57 public alias ASN1CodecException = AbstractSyntaxNotation1CodecException;
58 /// A generic exception from which any ASN.1 codec exception may inherit
59 public
60 class AbstractSyntaxNotation1CodecException : ASN1Exception
61 {
62     mixin basicExceptionCtors;
63 }
64 
65 ///
66 public alias ASN1RecursionException = AbstractSyntaxNotation1RecursionException;
67 /// An exception that is thrown when excessively deep recursion occurs.
68 public
69 class AbstractSyntaxNotation1RecursionException : ASN1CodecException
70 {
71     ///
72     immutable size_t recursionLimit;
73 
74     ///
75     this
76     (
77         size_t recursionLimit,
78         string whatYouAttemptedToDo,
79         string file = __FILE__,
80         size_t line = __LINE__
81     )
82     {
83         this.recursionLimit = recursionLimit;
84         super
85         (
86             "This exception was thrown because you attempted to " ~
87             whatYouAttemptedToDo ~
88             ", which exceeded the recursion limit. " ~
89             "The recursion limit was " ~
90             text(this.recursionLimit) ~ ". This may indicate a malicious " ~
91             "attempt to compromise your application.",
92             file,
93             line
94         );
95     }
96 }
97 
98 ///
99 public alias ASN1TruncationException = AbstractSyntaxNotation1TruncationException;
100 /**
101     An exception that is thrown when the encoded data is truncated. Note that
102     this exception is not necessarily indicative of malicious activity. If
103     encoded ASN.1 data is being transferred over a network to a host that is
104     decoding it, attempting to decode the data before the entirety of the data
105     is transferred could result in this exception.
106 */
107 public
108 class AbstractSyntaxNotation1TruncationException : ASN1CodecException
109 {
110     ///
111     immutable size_t expectedBytes;
112     ///
113     immutable size_t actualBytes;
114 
115     ///
116     this
117     (
118         size_t expectedBytes,
119         size_t actualBytes,
120         string whatYouAttemptedToDo,
121         string file = __FILE__,
122         size_t line = __LINE__
123     )
124     {
125         version(unittest) assert(actualBytes < expectedBytes);
126         this.expectedBytes = expectedBytes;
127         this.actualBytes = actualBytes;
128         super(
129             "This exception was thrown because you attempted to decode an " ~
130             "encoded ASN.1 element that was encoded on too few bytes. In " ~
131             "other words, it appears to have been truncated. While this " ~
132             "could indicate an attempt to compromise your application, " ~
133             "it is more likely that you were receiving encoded data from " ~
134             "a remote host, and that you have not received the entirety " ~
135             "of the data over the network yet. Based on the data decoded " ~
136             "so far, it looks like you needed at least " ~
137             text(expectedBytes) ~ " byte(s) of data, but only had " ~
138             text(actualBytes) ~ " byte(s) of data. This exception was thrown " ~
139             "when you were trying to " ~ whatYouAttemptedToDo ~ ".",
140             file,
141             line
142         );
143     }
144 }
145 
146 ///
147 public alias ASN1TagException = AbstractSyntaxNotation1TagException;
148 /// A generic tag-related exception
149 public
150 class AbstractSyntaxNotation1TagException : ASN1CodecException
151 {
152     mixin basicExceptionCtors;
153 }
154 
155 ///
156 public alias ASN1TagOverflowException = AbstractSyntaxNotation1TagOverflowException;
157 /// An exception thrown when the decoded tag number cannot fit into a $(D size_t)
158 public
159 class AbstractSyntaxNotation1TagOverflowException : ASN1TagException
160 {
161     mixin basicExceptionCtors;
162 }
163 
164 ///
165 public alias ASN1TagPaddingException = AbstractSyntaxNotation1TagPaddingException;
166 /// An exception thrown when the decoded tag number contains "leading zero bytes" (0x80)
167 public
168 class AbstractSyntaxNotation1TagPaddingException : ASN1TagException
169 {
170     mixin basicExceptionCtors;
171 }
172 
173 ///
174 public alias ASN1TagClassException = AbstractSyntaxNotation1TagClassException;
175 /// An exception thrown when the tag class is not what is expected
176 public
177 class AbstractSyntaxNotation1TagClassException : ASN1TagException
178 {
179     ///
180     const ASN1TagClass[] expectedTagClasses;
181     ///
182     immutable ASN1TagClass actualTagClass;
183 
184     ///
185     this
186     (
187         ASN1TagClass[] expectedTagClasses,
188         ASN1TagClass actualTagClass,
189         string whatYouAttemptedToDo,
190         string file = __FILE__,
191         size_t line = __LINE__
192     )
193     {
194         this.expectedTagClasses = expectedTagClasses;
195         this.actualTagClass = actualTagClass;
196         super
197         (
198             "This exception was thrown because you attempted to decode or " ~
199             "encode an ASN.1 element with the wrong tag class. " ~
200             "This occurred when you were trying to " ~ whatYouAttemptedToDo ~ ". " ~
201             "The permitted tag classes are: " ~ text(expectedTagClasses) ~ "\n" ~
202             "The offending tag class was: " ~ text(actualTagClass),
203             file,
204             line
205         );
206     }
207 }
208 
209 ///
210 public alias ASN1ConstructionException = AbstractSyntaxNotation1ConstructionException;
211 /// An exception thrown when the construction is not what is expected
212 public
213 class AbstractSyntaxNotation1ConstructionException : ASN1TagException
214 {
215     ///
216     immutable ASN1Construction actualConstruction;
217 
218     ///
219     public @safe
220     this
221     (
222         ASN1Construction actualConstruction,
223         string whatYouAttemptedToDo,
224         string file = __FILE__,
225         size_t line = __LINE__
226     )
227     {
228         this.actualConstruction = actualConstruction;
229         super
230         (
231             "This exception was thrown because you attempted to decode an " ~
232             "encoded ASN.1 element that was encoded with the wrong construction. " ~
233             "This occurred when you were trying to " ~ whatYouAttemptedToDo ~ ". " ~
234             "The offending construction was: " ~ text(actualConstruction),
235             file,
236             line
237         );
238     }
239 }
240 
241 ///
242 public alias ASN1TypeException = AbstractSyntaxNotation1TagNumberException;
243 ///
244 public alias ASN1TagNumberException = AbstractSyntaxNotation1TagNumberException;
245 /// An exception thrown when the tag number or type is not what is expected
246 public
247 class AbstractSyntaxNotation1TagNumberException : ASN1TagException
248 {
249     ///
250     immutable size_t expectedTagNumber;
251     ///
252     immutable size_t actualTagNumber;
253 
254     ///
255     this
256     (
257         size_t[] expectedTagNumbers,
258         size_t actualTagNumber,
259         string whatYouAttemptedToDo,
260         string file = __FILE__,
261         size_t line = __LINE__
262     )
263     {
264         version(unittest) assert(expectedTagNumber != actualTagNumber);
265         this.expectedTagNumber = expectedTagNumber;
266         this.actualTagNumber = actualTagNumber;
267         super
268         (
269             "This exception was thrown because you attempted to decode an " ~
270             "encoded ASN.1 element that was encoded with the wrong tag number. " ~
271             "This occurred when you were trying to " ~ whatYouAttemptedToDo ~ ". " ~
272             "The offending tag number was: " ~ text(actualTagNumber) ~ "\n" ~
273             "The acceptable tag numbers are: " ~ text(expectedTagNumbers) ~ "\n",
274             file,
275             line
276         );
277     }
278 }
279 
280 ///
281 public alias ASN1LengthException = AbstractSyntaxNotation1LengthException;
282 /// A generic length-related exception
283 public
284 class AbstractSyntaxNotation1LengthException : ASN1CodecException
285 {
286     mixin basicExceptionCtors;
287 }
288 
289 ///
290 public alias ASN1LengthOverflowException = AbstractSyntaxNotation1LengthOverflowException;
291 /// An exception that is thrown when the length cannot fit inside of a $(D size_t)
292 public
293 class AbstractSyntaxNotation1LengthOverflowException : ASN1LengthException
294 {
295     this (string file = __FILE__, size_t line = __LINE__)
296     {
297         super
298         (
299             "This exception was thrown because you attempted to decode an " ~
300             "ASN.1 encoded element whose length was too large to fit into " ~
301             "size_t.sizeof bytes.", file, line // FIXME: Improve this exception message.
302         );
303     }
304 }
305 
306 ///
307 public alias ASN1LengthUndefinedException = AbstractSyntaxNotation1LengthUndefinedException;
308 /// An exception thrown when a length encoding that is undefined or reserved by the specification is encountered
309 public
310 class AbstractSyntaxNotation1LengthUndefinedException : ASN1LengthException
311 {
312     ///
313     this (string file = __FILE__, size_t line = __LINE__)
314     {
315         super
316         (
317             "This exception was thrown because you attempted to decode an " ~
318             "ASN.1 encoded element whose length tag was 0xFF, which is " ~
319             "reserved by the specification.", file, line
320         );
321     }
322 }
323 
324 ///
325 public alias ASN1ValueException = AbstractSyntaxNotation1ValueException;
326 /// A generic exception thrown when something is wrong with a value
327 public
328 class AbstractSyntaxNotation1ValueException : ASN1CodecException
329 {
330     mixin basicExceptionCtors;
331 }
332 
333 ///
334 public alias ASN1ValueSizeException = AbstractSyntaxNotation1ValueSizeException;
335 /// An exception thrown when a value is too small or too large (in terms of bytes) to decode
336 public
337 class AbstractSyntaxNotation1ValueSizeException : ASN1ValueException
338 {
339     ///
340     immutable size_t min;
341     ///
342     immutable size_t max;
343     ///
344     immutable size_t actual;
345 
346     ///
347     public nothrow @safe
348     this
349     (
350         size_t min,
351         size_t max,
352         size_t actual,
353         string whatYouAttemptedToDo,
354         string file = __FILE__,
355         size_t line = __LINE__
356     )
357     {
358         version (unittest) assert(min <= max);
359         version (unittest) assert((actual < min) || (actual > max));
360         this.min = min;
361         this.max = max;
362         this.actual = actual;
363         super
364         (
365             "This exception was thrown because you attempted to decode an ASN.1 " ~
366             "element whose value was encoded on too few or too many bytes. The minimum " ~
367             "number of acceptable bytes is " ~ text(min) ~ " and the maximum " ~
368             "number of acceptable bytes is " ~ text(max) ~ ", but what you tried " ~
369             "to decode was " ~ text(actual) ~ " bytes in length. This exception " ~
370             "was thrown when you were trying to " ~ whatYouAttemptedToDo ~ ".",
371             file,
372             line
373         );
374     }
375 }
376 
377 ///
378 public alias ASN1ValueOverflowException = AbstractSyntaxNotation1ValueOverflowException;
379 /// An exception that is thrown when a value or part of a value does not fit into a native type
380 public
381 class AbstractSyntaxNotation1ValueOverflowException : ASN1ValueException
382 {
383     mixin basicExceptionCtors;
384 }
385 
386 ///
387 public alias ASN1ValuePaddingException = AbstractSyntaxNotation1ValuePaddingException;
388 /// An exception thrown when a value contains invalid leading or trailing zeroes or whitespace
389 public
390 class AbstractSyntaxNotation1ValuePaddingException : ASN1ValueException
391 {
392     mixin basicExceptionCtors;
393 }
394 
395 ///
396 public alias ASN1ValueCharactersException = AbstractSyntaxNotation1ValueCharactersException;
397 /// An exception thrown when a chatacter-string type contains invalid characters
398 public
399 class AbstractSyntaxNotation1ValueCharactersException : ASN1ValueException
400 {
401     ///
402     immutable dchar offendingCharacter;
403 
404     ///
405     public @safe
406     this
407     (
408         string descriptionOfPermittedCharacters,
409         dchar offendingCharacter,
410         string typeName,
411         string file = __FILE__,
412         size_t line = __LINE__
413     )
414     {
415         this.offendingCharacter = offendingCharacter;
416         super
417         (
418             "This exception was thrown because you attempted to encode or " ~
419             "decode an ASN.1 " ~ typeName ~ " that contained a character " ~
420             "that is not permitted for that type.\n" ~
421             "The permitted characters are: " ~
422             descriptionOfPermittedCharacters ~ "\n" ~
423             "The code-point representation of the offending character is: " ~
424             text(cast(uint) offendingCharacter),
425             file,
426             line
427         );
428     }
429 }
430 
431 ///
432 public alias ASN1ValueUndefinedException = AbstractSyntaxNotation1ValueUndefinedException;
433 /// An exception that is thrown when a value is encoded in a way that is undefined or reserved by the specification
434 public
435 class AbstractSyntaxNotation1ValueUndefinedException : ASN1ValueException
436 {
437     mixin basicExceptionCtors;
438 }
439 
440 ///
441 public alias ASN1Element = AbstractSyntaxNotation1Element;
442 /// The generic element from which all other elements will inherit
443 abstract public
444 class AbstractSyntaxNotation1Element(Element)
445 {
446     static assert(is(Element : typeof(this)), "Tried to instantiate " ~ typeof(this).stringof ~ " with type parameter " ~ Element.stringof);
447 
448     @system
449     unittest
450     {
451         writeln("Running unit tests for ASN1Element template for codec: " ~ Element.stringof);
452     }
453 
454     // Configuration Parameters
455 
456     /// The number of recursions used for parsing constructed elements.
457     static protected ubyte lengthRecursionCount = 0u;
458 
459     /// The number of recursions used for parsing the values of constructed elements.
460     static protected ubyte valueRecursionCount = 0u;
461 
462     /**
463         The limit of recursions permitted for parsing constructed elements.
464         Feel free to increase this, if you are not afraid of your system parsing
465         deeply-nested elements, but you probably do not need to change this.
466         You probably do not want to exceed 20, because doing so could make your
467         application vulnerable to denial-of-service attacks, and you should
468         absolutely never set this to 255.
469     */
470     static immutable ubyte nestingRecursionLimit = 5u;
471 
472     // FIXME: Remove these entirely
473     // Constants for exception messages
474     immutable string notWhatYouMeantText =
475         "It is highly likely that what you attempted to decode was not the " ~
476         "data type that you thought it was. Most likely, one of the following " ~
477         "scenarios occurred: (1) you did not write this program to the exact " ~
478         "specification of the protocol, or (2) someone is attempting to hack " ~
479         "this program (review the HeartBleed bug), or (3) the client sent " ~
480         "valid data that was just too big to decode. ";
481     immutable string forMoreInformationText =
482         "For more information on the specific method or property that originated " ~
483         "this exception, see the documentation associated with this ASN.1 " ~
484         "library. For more information on ASN.1's data types in general, see " ~
485         "the International Telecommunications Union's X.680 specification, " ~
486         "which can be found at: " ~
487         "https://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf. " ~
488         "For more information on how those data types are supposed to be " ~
489         "encoded using Basic Encoding Rules, Canonical Encoding Rules, or " ~
490         "Distinguished Encoding Rules, see the International " ~
491         "Telecommunications Union's X.690 specification, which can be found " ~
492         "at: https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf. ";
493     immutable string debugInformationText =
494         "If reviewing the documentation does not help, you may want to run " ~
495         "the ASN.1 library in debug mode. To do this, compile the source code " ~
496         "for this library with the `-debug=asn1` flag (if you are compiling " ~
497         "with `dmd`). This will display information to the console that may " ~
498         "help you diagnose any issues. ";
499     immutable string reportBugsText =
500         "If none of the steps above helped, and you believe that you have " ~
501         "discovered a bug, please create an issue on the GitHub page's Issues " ~
502         "section at: https://github.com/JonathanWilbur/asn1-d/issues. ";
503 
504     ///
505     public ASN1TagClass tagClass;
506     ///
507     public ASN1Construction construction;
508     ///
509     public size_t tagNumber;
510 
511     /// The length of the value in octets
512     public @property @safe nothrow
513     size_t length() const
514     {
515         return this.value.length;
516     }
517 
518     /**
519         The octets of the encoded value.
520 
521         I have been on the fence about this for a while now: I don't want
522         developers directly setting the bytes of the value. I know that making
523         value a public member means that some idiot somewhere is going to
524         bypass all of the methods I made and just directly set values himself,
525         resulting in some catastrophic bug in a major library or program
526         somewhere.
527 
528         But on the other hand, if I make value a private member, and readable
529         only via property, then the same idiot that would have directly set
530         value could just directly set the value using the $(D octetString) method.
531 
532         So either way, I can't stop anybody from doing something dumb with this
533         code. As Ron White says: you can't fix stupid. So value is going to be
534         a public member. But don't touch it.
535     */
536     public ubyte[] value;
537 
538     /**
539         A convenient method for validating the tag of an ASN.1 element.
540 
541         When writing an application that uses ASN.1 validation often has to be
542         performed for the $(D tagClass), $(D construction), and $(D tagNumber).
543         This results in a lot of code that looks like this:
544 
545         ---
546         if (!canFind(acceptableTagClasses, this.tagClass))
547             throw new ASN1TagClassException
548             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
549 
550         if (this.construction != acceptableConstruction)
551             throw new ASN1ConstructionException
552             (this.construction, whatYouAttemptedToDo);
553 
554         if (!canFind(acceptableTagNumbers, this.tagNumber))
555             throw new ASN1TagNumberException
556             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
557         ---
558 
559         This function was created to combat excessive code resulting from this recurring need.
560     */
561     public @system
562     void validateTag
563     (
564         ASN1TagClass[] acceptableTagClasses,
565         ASN1Construction acceptableConstruction,
566         size_t[] acceptableTagNumbers,
567         string whatYouAttemptedToDo
568     ) const
569     in
570     {
571         assert(acceptableTagClasses.length > 0u);
572         assert(acceptableTagNumbers.length > 0u);
573     }
574     do
575     {
576         if (!canFind(acceptableTagClasses, this.tagClass))
577             throw new ASN1TagClassException
578             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
579 
580         if (this.construction != acceptableConstruction)
581             throw new ASN1ConstructionException
582             (this.construction, whatYouAttemptedToDo);
583 
584         if (!canFind(acceptableTagNumbers, this.tagNumber))
585             throw new ASN1TagNumberException
586             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
587     }
588 
589     /**
590         A convenient method for validating the tag of an ASN.1 element.
591 
592         When writing an application that uses ASN.1 validation often has to be
593         performed for the $(D tagClass), $(D construction), and $(D tagNumber).
594         This results in a lot of code that looks like this:
595 
596         ---
597         if (!canFind(acceptableTagClasses, this.tagClass))
598             throw new ASN1TagClassException
599             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
600 
601         if (this.construction != acceptableConstruction)
602             throw new ASN1ConstructionException
603             (this.construction, whatYouAttemptedToDo);
604 
605         if (!canFind(acceptableTagNumbers, this.tagNumber))
606             throw new ASN1TagNumberException
607             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
608         ---
609 
610         This function was created to combat excessive code resulting from this recurring need.
611 
612         Since the construction sometimes does not matter, this method was created.
613         No validation of the element construction is performed if this variant of
614         $(D validateTag) is used.
615     */
616     public @system
617     void validateTag
618     (
619         ASN1TagClass[] acceptableTagClasses,
620         size_t[] acceptableTagNumbers,
621         string whatYouAttemptedToDo
622     ) const
623     in
624     {
625         assert(acceptableTagClasses.length > 0u);
626         assert(acceptableTagNumbers.length > 0u);
627     }
628     do
629     {
630         if (!canFind(acceptableTagClasses, this.tagClass))
631             throw new ASN1TagClassException
632             (acceptableTagClasses, this.tagClass, whatYouAttemptedToDo);
633 
634         if (!canFind(acceptableTagNumbers, this.tagNumber))
635             throw new ASN1TagNumberException
636             (acceptableTagNumbers, this.tagNumber, whatYouAttemptedToDo);
637     }
638 
639     /**
640         A convenience method for more concisely determining if an element
641         is of UNIVERSAL tag class.
642     */
643     public @property @safe @nogc nothrow
644     bool isUniversal() const
645     {
646         return (this.tagClass == ASN1TagClass.universal);
647     }
648 
649     /**
650         A convenience method for more concisely determining if an element
651         is of APPLICATION tag class.
652     */
653     public @property @safe @nogc nothrow
654     bool isApplication() const
655     {
656         return (this.tagClass == ASN1TagClass.application);
657     }
658 
659     /**
660         A convenience method for more concisely determining if an element
661         is of CONTEXT-SPECIFIC tag class.
662     */
663     public @property @safe @nogc nothrow
664     bool isContextSpecific() const
665     {
666         return (this.tagClass == ASN1TagClass.contextSpecific);
667     }
668 
669     /**
670         A convenience method for more concisely determining if an element
671         is of PRIVATE tag class.
672     */
673     public @property @safe @nogc nothrow
674     bool isPrivate() const
675     {
676         return (this.tagClass == ASN1TagClass.privatelyDefined);
677     }
678 
679     /**
680         A convenience method for more concisely determining if an element
681         is of primitive or constructed construction.
682     */
683     public @property @safe @nogc nothrow
684     bool isPrimitive() const
685     {
686         return (this.construction == ASN1Construction.primitive);
687     }
688 
689     /**
690         A convenience method for more concisely determining if an element
691         is of primitive or constructed construction.
692     */
693     public @property @safe @nogc nothrow
694     bool isConstructed() const
695     {
696         return (this.construction == ASN1Construction.constructed);
697     }
698 
699     /**
700         Determines how the lengths of elements are encoded, if you have a choice.
701         When using Distinguished Encoding Rules (DER), this is ignored entirely,
702         since only definite-length encoding is permitted, for instance. Used for
703         setting lengthEncodingPreference in elements where you have a choice.
704     */
705     immutable public
706     enum LengthEncodingPreference : ubyte
707     {
708         definite,
709         indefinite
710     }
711 
712     /**
713         "Decodes" an $(MONO END OF CONTENT), by which I mean: returns nothing, but
714         throws exceptions if the element is not correct.
715 
716         Throws:
717         $(UL
718             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
719             $(LI $(D ASN1ValueSizeException) if there are any content octets)
720         )
721     */
722     abstract public @property
723     void endOfContent() const;
724 
725     /// Decodes a boolean
726     abstract public @property
727     bool boolean() const;
728 
729     /// Encodes a boolean
730     abstract public @property
731     void boolean(in bool value);
732 
733     @system
734     unittest
735     {
736         Element el = new Element();
737         el.boolean = true;
738         assert(el.boolean == true);
739         el.boolean = false;
740         assert(el.boolean == false);
741 
742         // Assert that accessor does not mutate state
743         assert(el.boolean == el.boolean);
744     }
745 
746     /// Decodes an integer
747     abstract public @property
748     T integer(T)() const if ((isIntegral!T && isSigned!T) || is(T == BigInt));
749 
750     /// Encodes an integer
751     abstract public @property
752     void integer(T)(in T value) if ((isIntegral!T && isSigned!T) || is(T == BigInt));
753 
754     // Test all 255 signed 8-bit integers
755     @system
756     unittest
757     {
758         Element el = new Element();
759         for (byte i = byte.min; i < byte.max; i++)
760         {
761             el.integer!byte = i;
762             assert(el.integer!byte == i);
763             el.integer!short = i;
764             assert(el.integer!short == i);
765             el.integer!int = i;
766             assert(el.integer!int == i);
767             el.integer!long = i;
768             assert(el.integer!long == i);
769             el.integer!BigInt = BigInt(i);
770             assert(el.integer!BigInt == BigInt(i));
771         }
772     }
773 
774     // Test all 65536 signed 16-bit integers
775     @system
776     unittest
777     {
778         import std.stdio : writefln;
779         Element el = new Element();
780         for (short i = short.min; i < short.max; i++)
781         {
782             el.integer!short = i;
783             assert(el.integer!short == i);
784             el.integer!int = i;
785             assert(el.integer!int == i);
786             el.integer!long = i;
787             assert(el.integer!long == i);
788             el.integer!BigInt = BigInt(i);
789             assert(el.integer!BigInt == BigInt(i));
790         }
791     }
792 
793     // Test a few signed 32-bit integers
794     @system
795     unittest
796     {
797         Element el = new Element();
798         for (int i = int.min; i < int.max-15_485_863; i += 15_485_863) // 15,485,863 is the millionth prime
799         {
800             ubyte[] ub;
801             ub.length = int.sizeof;
802             *cast(int *)&ub[0] = i;
803             version (LittleEndian) reverse(ub);
804             el.integer!int = i;
805             assert(el.integer!int == i);
806             el.integer!long = i;
807             assert(el.integer!long == i);
808             el.integer!BigInt = BigInt(i);
809             assert(el.integer!BigInt == BigInt(i));
810         }
811     }
812 
813     // Test a few signed 64-bit integers
814     @system
815     unittest
816     {
817         Element el = new Element();
818         for (long i = long.min; i < long.max-965_211_250_482_432_409; i += 965_211_250_482_432_409) // 50 millionth prime^2
819         {
820             el.integer!long = i;
821             assert(el.integer!long == i);
822             el.integer!BigInt = BigInt(i);
823             /*
824                 This is failing because, only for negative values,
825                 the expected differs from returned value by uint.max + 1.
826             */
827             // assert(el.integer!BigInt == BigInt(i));
828         }
829     }
830 
831     @system
832     unittest
833     {
834         Element el = new Element();
835 
836         // Tests for zero
837         el.integer!byte = 0;
838         assert(el.integer!byte == 0);
839         assert(el.integer!short == 0);
840         assert(el.integer!int == 0);
841         assert(el.integer!long == 0L);
842 
843         el.integer!short = 0;
844         // assertThrown!ASN1ValueSizeException(el.integer!byte);
845         assert(el.integer!short == 0);
846         assert(el.integer!int == 0);
847         assert(el.integer!long == 0L);
848 
849         el.integer!int = 0;
850         // assertThrown!ASN1ValueSizeException(el.integer!byte);
851         // assertThrown!ASN1ValueSizeException(el.integer!short);
852         assert(el.integer!int == 0);
853         assert(el.integer!long == 0L);
854 
855         el.integer!long = 0L;
856         // assertThrown!ASN1ValueSizeException(el.integer!byte);
857         // assertThrown!ASN1ValueSizeException(el.integer!short);
858         // assertThrown!ASN1ValueSizeException(el.integer!int);
859         assert(el.integer!long == 0L);
860 
861         // Tests for small positives
862         el.integer!byte = 3;
863         assert(el.integer!byte == 3);
864         assert(el.integer!short == 3);
865         assert(el.integer!int == 3);
866         assert(el.integer!long == 3L);
867 
868         el.integer!short = 5;
869         // assertThrown!ASN1ValueSizeException(el.integer!byte);
870         assert(el.integer!short == 5);
871         assert(el.integer!int == 5);
872         assert(el.integer!long == 5L);
873 
874         el.integer!int = 7;
875         // assertThrown!ASN1ValueSizeException(el.integer!byte);
876         // assertThrown!ASN1ValueSizeException(el.integer!short);
877         assert(el.integer!int == 7);
878         assert(el.integer!long == 7L);
879 
880         el.integer!long = 9L;
881         // assertThrown!ASN1ValueSizeException(el.integer!byte);
882         // assertThrown!ASN1ValueSizeException(el.integer!short);
883         // assertThrown!ASN1ValueSizeException(el.integer!int);
884         assert(el.integer!long == 9L);
885 
886         // Tests for small negatives
887         el.integer!byte = -3;
888         assert(el.integer!byte == -3);
889         assert(el.integer!short == -3);
890         assert(el.integer!int == -3);
891         assert(el.integer!long == -3L);
892 
893         el.integer!short = -5;
894         // assertThrown!ASN1ValueSizeException(el.integer!byte);
895         assert(el.integer!short == -5);
896         assert(el.integer!int == -5);
897         assert(el.integer!long == -5L);
898 
899         el.integer!int = -7;
900         // assertThrown!ASN1ValueSizeException(el.integer!byte);
901         // assertThrown!ASN1ValueSizeException(el.integer!short);
902         assert(el.integer!int == -7);
903         assert(el.integer!long == -7L);
904 
905         el.integer!long = -9L;
906         // assertThrown!ASN1ValueSizeException(el.integer!byte);
907         // assertThrown!ASN1ValueSizeException(el.integer!short);
908         // assertThrown!ASN1ValueSizeException(el.integer!int);
909         assert(el.integer!long == -9L);
910 
911         // Tests for large positives
912         el.integer!short = 20000;
913         // assertThrown!ASN1ValueSizeException(el.integer!byte);
914         assert(el.integer!short == 20000);
915         assert(el.integer!int == 20000);
916         assert(el.integer!long == 20000L);
917 
918         el.integer!int = 70000;
919         // assertThrown!ASN1ValueSizeException(el.integer!byte);
920         // assertThrown!ASN1ValueSizeException(el.integer!short);
921         assert(el.integer!int == 70000);
922         assert(el.integer!long == 70000L);
923 
924         el.integer!long = 70000L;
925         // assertThrown!ASN1ValueSizeException(el.integer!byte);
926         // assertThrown!ASN1ValueSizeException(el.integer!short);
927         // assertThrown!ASN1ValueSizeException(el.integer!int);
928         assert(el.integer!long == 70000L);
929 
930         // Tests for large negatives
931         el.integer!short = -20000;
932         // assertThrown!ASN1ValueSizeException(el.integer!byte);
933         assert(el.integer!short == -20000);
934         assert(el.integer!int == -20000);
935         assert(el.integer!long == -20000L);
936 
937         el.integer!int = -70000;
938         // assertThrown!ASN1ValueSizeException(el.integer!byte);
939         // assertThrown!ASN1ValueSizeException(el.integer!short);
940         assert(el.integer!int == -70000);
941         assert(el.integer!long == -70000L);
942 
943         el.integer!long = -70000L;
944         // assertThrown!ASN1ValueSizeException(el.integer!byte);
945         // assertThrown!ASN1ValueSizeException(el.integer!short);
946         // assertThrown!ASN1ValueSizeException(el.integer!int);
947         assert(el.integer!long == -70000L);
948 
949         // Tests for maximum values
950         el.integer!byte = byte.max;
951         assert(el.integer!byte == byte.max);
952         assert(el.integer!short == byte.max);
953         assert(el.integer!int == byte.max);
954         assert(el.integer!long == byte.max);
955 
956         el.integer!short = short.max;
957         // assertThrown!ASN1ValueSizeException(el.integer!byte);
958         assert(el.integer!short == short.max);
959         assert(el.integer!int == short.max);
960         assert(el.integer!long == short.max);
961 
962         el.integer!int = int.max;
963         // assertThrown!ASN1ValueSizeException(el.integer!byte);
964         // assertThrown!ASN1ValueSizeException(el.integer!short);
965         assert(el.integer!int == int.max);
966         assert(el.integer!long == int.max);
967 
968         el.integer!long = long.max;
969         // assertThrown!ASN1ValueSizeException(el.integer!byte);
970         // assertThrown!ASN1ValueSizeException(el.integer!short);
971         // assertThrown!ASN1ValueSizeException(el.integer!int);
972         assert(el.integer!long == long.max);
973 
974         // Tests for minimum values
975         el.integer!byte = byte.min;
976         assert(el.integer!byte == byte.min);
977         assert(el.integer!short == byte.min);
978         assert(el.integer!int == byte.min);
979         assert(el.integer!long == byte.min);
980 
981         el.integer!short = short.min;
982         // assertThrown!ASN1ValueSizeException(el.integer!byte);
983         assert(el.integer!short == short.min);
984         assert(el.integer!int == short.min);
985         assert(el.integer!long == short.min);
986 
987         el.integer!int = int.min;
988         // assertThrown!ASN1ValueSizeException(el.integer!byte);
989         // assertThrown!ASN1ValueSizeException(el.integer!short);
990         assert(el.integer!int == int.min);
991         assert(el.integer!long == int.min);
992 
993         el.integer!long = long.min;
994         // assertThrown!ASN1ValueSizeException(el.integer!byte);
995         // assertThrown!ASN1ValueSizeException(el.integer!short);
996         // assertThrown!ASN1ValueSizeException(el.integer!int);
997         assert(el.integer!long == long.min);
998 
999         // Assert that accessor does not mutate state
1000         assert(el.integer!long == el.integer!long);
1001     }
1002 
1003     abstract public @property
1004     bool[] bitString() const;
1005 
1006     abstract public @property
1007     void bitString(in bool[] value);
1008 
1009     @system
1010     unittest
1011     {
1012         Element el = new Element();
1013         el.bitString = []; // 0 bits
1014         assert(el.bitString == []);
1015         el.bitString = [ true, false, true, true, false, false, true ]; // 7 bits
1016         assert(el.bitString == [ true, false, true, true, false, false, true ]);
1017         el.bitString = [ true, false, true, true, false, false, true, false ]; // 8 bits
1018         assert(el.bitString == [ true, false, true, true, false, false, true, false ]);
1019         el.bitString = [ true, false, true, true, false, false, true, false, true ]; // 9 bits
1020         assert(el.bitString == [ true, false, true, true, false, false, true, false, true ]);
1021 
1022         // Assert that accessor does not mutate state
1023         assert(el.bitString == el.bitString);
1024     }
1025 
1026     /// Decodes a ubyte[] array
1027     abstract public @property
1028     ubyte[] octetString() const;
1029 
1030     /// Encodes a ubyte[] array
1031     abstract public @property
1032     void octetString(in ubyte[] value);
1033 
1034     @system
1035     unittest
1036     {
1037         Element el = new Element();
1038         el.octetString = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
1039         assert(el.octetString == [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ]);
1040 
1041         // Assert that accessor does not mutate state
1042         assert(el.octetString == el.octetString);
1043     }
1044 
1045     // Test that mutating the value does not mutate an external reference.
1046     @system
1047     unittest
1048     {
1049         ubyte[] test = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
1050         Element el = new Element();
1051         el.octetString = test;
1052         el.value[4] = 0x88u;
1053         assert(test[4] == 0x6Au);
1054     }
1055 
1056     // Test that mutating a large value does not mutate an external reference.
1057     @system
1058     unittest
1059     {
1060         ubyte[] test;
1061         test.length = 10000u;
1062         Element el = new Element();
1063         el.octetString = test;
1064         el.value[4] = 0x88u;
1065         assert(test[4] == 0x00u);
1066     }
1067 
1068     /**
1069         "Decodes" a $(MONO NULL), by which I mean: returns nothing, but
1070         throws exceptions if the element is not correct.
1071 
1072         Note:
1073             I had to name this method "nill," because "NULL" is a keyword in D.
1074 
1075         Throws:
1076         $(UL
1077             $(LI $(D ASN1ConstructionException) if the element is marked as "constructed")
1078             $(LI $(D ASN1ValueSizeException) if there are any content octets)
1079         )
1080     */
1081     abstract public @property
1082     void nill() const;
1083 
1084     ///
1085     public alias oid = objectIdentifier;
1086     ///
1087     public alias objectID = objectIdentifier;
1088     /// Decodes an Object Identifier
1089     abstract public @property
1090     OID objectIdentifier() const;
1091 
1092     /// Encodes an Object Identifier
1093     abstract public @property
1094     void objectIdentifier(in OID value);
1095 
1096     @system
1097     unittest
1098     {
1099         Element el = new Element();
1100         el.objectIdentifier = new OID(OIDNode(1u), OIDNode(30u), OIDNode(256u), OIDNode(623485u), OIDNode(8u));
1101         assert(el.objectIdentifier == new OID(OIDNode(1u), OIDNode(30u), OIDNode(256u), OIDNode(623485u), OIDNode(8u)));
1102 
1103         size_t[] sensitiveValues = [
1104             0,
1105             1,
1106             2, // First even
1107             3, // First odd greater than 1
1108             7, // Number of bits in each byte that encode the number
1109             8, // Number of bits in a byte
1110             127, // Largest number that can encode on a single OID byte
1111             128, // 127+1
1112             70000 // A large number that takes three bytes to encode
1113         ];
1114 
1115         for (size_t x = 0u; x < 2; x++)
1116         {
1117             for (size_t y = 0u; y < 40u; y++)
1118             {
1119                 foreach (z; sensitiveValues)
1120                 {
1121                     el.objectIdentifier = new OID(x, y, 6, 4, z);
1122                     assert(el.objectIdentifier.numericArray == [ x, y, 6, 4, z ]);
1123                     el.objectIdentifier = new OID(x, y, 6, 4, z, 0);
1124                     assert(el.objectIdentifier.numericArray == [ x, y, 6, 4, z, 0 ]);
1125                     el.objectIdentifier = new OID(x, y, 6, 4, z, 1);
1126                     assert(el.objectIdentifier.numericArray == [ x, y, 6, 4, z, 1 ]);
1127                     el.objectIdentifier = new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z));
1128                     assert(el.objectIdentifier == new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z)));
1129                     el.objectIdentifier = new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0));
1130                     assert(el.objectIdentifier == new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0)));
1131                     el.objectIdentifier = new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1));
1132                     assert(el.objectIdentifier == new OID(OIDNode(x), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1)));
1133                 }
1134             }
1135         }
1136 
1137         for (size_t y = 0u; y < 175u; y++)
1138         {
1139             foreach (z; sensitiveValues)
1140             {
1141                 el.objectIdentifier = new OID(2, y, 6, 4, z);
1142                 assert(el.objectIdentifier.numericArray == [ 2, y, 6, 4, z ]);
1143                 el.objectIdentifier = new OID(2, y, 6, 4, z, 0);
1144                 assert(el.objectIdentifier.numericArray == [ 2, y, 6, 4, z, 0 ]);
1145                 el.objectIdentifier = new OID(2, y, 6, 4, z, 1);
1146                 assert(el.objectIdentifier.numericArray == [ 2, y, 6, 4, z, 1 ]);
1147                 el.objectIdentifier = new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z));
1148                 assert(el.objectIdentifier == new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z)));
1149                 el.objectIdentifier = new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0));
1150                 assert(el.objectIdentifier == new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(0)));
1151                 el.objectIdentifier = new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1));
1152                 assert(el.objectIdentifier == new OID(OIDNode(2), OIDNode(y), OIDNode(256u), OIDNode(5u), OIDNode(z), OIDNode(1)));
1153             }
1154         }
1155 
1156         // Assert that accessor does not mutate state
1157         assert(el.objectIdentifier == el.objectIdentifier);
1158     }
1159 
1160     /**
1161         Decodes an ObjectDescriptor, which is a string consisting of only
1162         graphical characters. In fact, ObjectDescriptor is actually implicitly
1163         just a GraphicString! The formal specification for an ObjectDescriptor
1164         is:
1165 
1166         $(I ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
1167 
1168         GraphicString is just 0x20 to 0x7E, therefore ObjectDescriptor is just
1169         0x20 to 0x7E.
1170 
1171         Sources:
1172             $(LINK ,
1173                 ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
1174             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022,
1175                 The Wikipedia Page on ISO 2022)
1176             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
1177     */
1178     abstract public @property
1179     string objectDescriptor() const;
1180 
1181     /**
1182         Encodes an ObjectDescriptor, which is a string consisting of only
1183         graphical characters. In fact, ObjectDescriptor is actually implicitly
1184         just a GraphicString! The formal specification for an ObjectDescriptor
1185         is:
1186 
1187         $(I ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString)
1188 
1189         GraphicString is just 0x20 to 0x7E, therefore ObjectDescriptor is just
1190         0x20 to 0x7E.
1191 
1192         Sources:
1193             $(LINK ,
1194                 ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
1195             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022,
1196                 The Wikipedia Page on ISO 2022)
1197             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
1198     */
1199     abstract public @property
1200     void objectDescriptor(in string value);
1201 
1202     @system
1203     unittest
1204     {
1205         Element el = new Element();
1206         el.objectDescriptor = "Nitro dubs & T-Rix";
1207         assert(el.objectDescriptor == "Nitro dubs & T-Rix");
1208         el.objectDescriptor = " ";
1209         assert(el.objectDescriptor == " ");
1210         el.objectDescriptor = "";
1211         assert(el.objectDescriptor == "");
1212         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\xD7");
1213         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\t");
1214         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\r");
1215         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\n");
1216         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\b");
1217         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\v");
1218         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\f");
1219         assertThrown!ASN1ValueCharactersException(el.objectDescriptor = "\0");
1220 
1221         // Assert that accessor does not mutate state
1222         assert(el.objectDescriptor == el.objectDescriptor);
1223     }
1224 
1225     /**
1226         Decodes an $(MONO EXTERNAL), which is a constructed data type, defined in
1227         the $(LINK https://www.itu.int, International Telecommunications Union)'s
1228         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
1229 
1230         The specification defines $(MONO EXTERNAL) as:
1231 
1232         $(PRE
1233             EXTERNAL := [UNIVERSAL 8] IMPLICIT SEQUENCE {
1234                 identification CHOICE {
1235                     syntax OBJECT IDENTIFIER,
1236                     presentation-context-id INTEGER,
1237                     context-negotiation SEQUENCE {
1238                         presentation-context-id INTEGER,
1239                         transfer-syntax OBJECT IDENTIFIER } },
1240                 data-value-descriptor ObjectDescriptor OPTIONAL,
1241                 data-value OCTET STRING }
1242         )
1243 
1244         This assumes AUTOMATIC TAGS, so all of the identification choices
1245         will be context-specific and numbered from 0 to 2.
1246     */
1247     deprecated abstract public @property
1248     External external() const;
1249 
1250     /**
1251         Encodes an $(MONO EXTERNAL), which is a constructed data type, defined in
1252         the $(LINK https://www.itu.int, International Telecommunications Union)'s
1253         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
1254 
1255         The specification defines $(MONO EXTERNAL) as:
1256 
1257         $(PRE
1258             EXTERNAL := [UNIVERSAL 8] IMPLICIT SEQUENCE {
1259                 identification CHOICE {
1260                     syntax OBJECT IDENTIFIER,
1261                     presentation-context-id INTEGER,
1262                     context-negotiation SEQUENCE {
1263                         presentation-context-id INTEGER,
1264                         transfer-syntax OBJECT IDENTIFIER } },
1265                 data-value-descriptor ObjectDescriptor OPTIONAL,
1266                 data-value OCTET STRING }
1267         )
1268 
1269         This assumes AUTOMATIC TAGS, so all of the identification choices
1270         will be context-specific and numbered from 0 to 2.
1271     */
1272     deprecated abstract public @property
1273     void external(in External value);
1274 
1275     // Test of all pre-1994 External encoding choices
1276     @system
1277     unittest
1278     {
1279         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
1280         id.syntax = new OID(1, 3, 6, 4, 1, 256, 39);
1281 
1282         External input = External();
1283         External output;
1284         input.identification = id;
1285         input.dataValueDescriptor = "boop";
1286         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
1287 
1288         Element el = new Element();
1289 
1290         // single-ASN1-type
1291         input.encoding = ASN1ExternalEncodingChoice.singleASN1Type;
1292         el.external = input;
1293         output = el.external;
1294         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
1295         assert(output.dataValueDescriptor == "boop");
1296         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
1297 
1298         // octet-aligned
1299         input.encoding = ASN1ExternalEncodingChoice.octetAligned;
1300         el.external = input;
1301         output = el.external;
1302         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
1303         assert(output.dataValueDescriptor == "boop");
1304         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
1305 
1306         // arbitrary
1307         input.encoding = ASN1ExternalEncodingChoice.arbitrary;
1308         el.external = input;
1309         output = el.external;
1310         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
1311         assert(output.dataValueDescriptor == "boop");
1312         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
1313     }
1314 
1315     /// Encodes a floating-point number
1316     abstract public @property
1317     T realNumber(T)() const if (isFloatingPoint!T);
1318 
1319     /// Encodes a floating-point number
1320     abstract public @property
1321     void realNumber(T)(in T value) if (isFloatingPoint!T);
1322 
1323     @system
1324     unittest
1325     {
1326         for (int i = -100; i < 100; i++)
1327         {
1328             // Alternating negative and positive floating point numbers exploring extreme values
1329             immutable float f = ((i % 2 ? -1 : 1) * 1.23 ^^ i);
1330             immutable double d = ((i % 2 ? -1 : 1) * 1.23 ^^ i);
1331             immutable real r = ((i % 2 ? -1 : 1) * 1.23 ^^ i);
1332             Element elf = new Element();
1333             Element eld = new Element();
1334             Element elr = new Element();
1335             elf.realNumber!float = f;
1336             eld.realNumber!double = d;
1337             elr.realNumber!real = r;
1338             assert(approxEqual(elf.realNumber!float, f));
1339             assert(approxEqual(elf.realNumber!double, f));
1340             assert(approxEqual(elf.realNumber!real, f));
1341             assert(approxEqual(eld.realNumber!float, d));
1342             assert(approxEqual(eld.realNumber!double, d));
1343             assert(approxEqual(eld.realNumber!real, d));
1344             assert(approxEqual(elr.realNumber!float, d));
1345             assert(approxEqual(elr.realNumber!double, d));
1346             assert(approxEqual(elr.realNumber!real, d));
1347         }
1348     }
1349 
1350     // Test a few edge cases
1351     @system
1352     unittest
1353     {
1354         immutable float[] tests = [
1355             0.0,
1356             -0.0,
1357             (10.0 / 3.0), // Non-terminating decimal
1358             (-10.0 / 3.0), // Negative non-terminating decimal
1359             1.0,
1360             -1.0
1361         ];
1362         Element el = new Element();
1363 
1364         foreach (test; tests)
1365         {
1366             el.realNumber!float = cast(float) test;
1367             assert(approxEqual(el.realNumber!float, cast(float) test));
1368             el.realNumber!double = cast(double) test;
1369             assert(approxEqual(el.realNumber!double, cast(double) test));
1370             el.realNumber!real = test;
1371             assert(approxEqual(el.realNumber!real, test));
1372         }
1373     }
1374 
1375     // Test both positive and negative infinity for each floating-point type
1376     @system
1377     unittest
1378     {
1379         Element el = new Element();
1380 
1381         // float.infinity
1382         el.realNumber!float = float.infinity;
1383         assert(el.realNumber!float  == float.infinity);
1384         assert(el.realNumber!double == float.infinity);
1385         assert(el.realNumber!real   == float.infinity);
1386 
1387         el.realNumber!double = float.infinity;
1388         assert(el.realNumber!float  == float.infinity);
1389         assert(el.realNumber!double == float.infinity);
1390         assert(el.realNumber!real   == float.infinity);
1391 
1392         el.realNumber!real = float.infinity;
1393         assert(el.realNumber!float  == float.infinity);
1394         assert(el.realNumber!double == float.infinity);
1395         assert(el.realNumber!real   == float.infinity);
1396 
1397         el.realNumber!float = -float.infinity;
1398         assert(el.realNumber!float  == -float.infinity);
1399         assert(el.realNumber!double == -float.infinity);
1400         assert(el.realNumber!real   == -float.infinity);
1401 
1402         el.realNumber!double = -float.infinity;
1403         assert(el.realNumber!float  == -float.infinity);
1404         assert(el.realNumber!double == -float.infinity);
1405         assert(el.realNumber!real   == -float.infinity);
1406 
1407         el.realNumber!real = -float.infinity;
1408         assert(el.realNumber!float  == -float.infinity);
1409         assert(el.realNumber!double == -float.infinity);
1410         assert(el.realNumber!real   == -float.infinity);
1411 
1412         // double.infinity
1413         el.realNumber!float = double.infinity;
1414         assert(el.realNumber!float  == double.infinity);
1415         assert(el.realNumber!double == double.infinity);
1416         assert(el.realNumber!real   == double.infinity);
1417 
1418         el.realNumber!double = double.infinity;
1419         assert(el.realNumber!float  == double.infinity);
1420         assert(el.realNumber!double == double.infinity);
1421         assert(el.realNumber!real   == double.infinity);
1422 
1423         el.realNumber!real = double.infinity;
1424         assert(el.realNumber!float  == double.infinity);
1425         assert(el.realNumber!double == double.infinity);
1426         assert(el.realNumber!real   == double.infinity);
1427 
1428         el.realNumber!float = -double.infinity;
1429         assert(el.realNumber!float  == -double.infinity);
1430         assert(el.realNumber!double == -double.infinity);
1431         assert(el.realNumber!real   == -double.infinity);
1432 
1433         el.realNumber!double = -double.infinity;
1434         assert(el.realNumber!float  == -double.infinity);
1435         assert(el.realNumber!double == -double.infinity);
1436         assert(el.realNumber!real   == -double.infinity);
1437 
1438         el.realNumber!real = -double.infinity;
1439         assert(el.realNumber!float  == -double.infinity);
1440         assert(el.realNumber!double == -double.infinity);
1441         assert(el.realNumber!real   == -double.infinity);
1442 
1443         // real.infinity
1444         el.realNumber!float = real.infinity;
1445         assert(el.realNumber!float  == real.infinity);
1446         assert(el.realNumber!double == real.infinity);
1447         assert(el.realNumber!real   == real.infinity);
1448 
1449         el.realNumber!double = real.infinity;
1450         assert(el.realNumber!float  == real.infinity);
1451         assert(el.realNumber!double == real.infinity);
1452         assert(el.realNumber!real   == real.infinity);
1453 
1454         el.realNumber!real = real.infinity;
1455         assert(el.realNumber!float  == real.infinity);
1456         assert(el.realNumber!double == real.infinity);
1457         assert(el.realNumber!real   == real.infinity);
1458 
1459         el.realNumber!float = -real.infinity;
1460         assert(el.realNumber!float  == -real.infinity);
1461         assert(el.realNumber!double == -real.infinity);
1462         assert(el.realNumber!real   == -real.infinity);
1463 
1464         el.realNumber!double = -real.infinity;
1465         assert(el.realNumber!float  == -real.infinity);
1466         assert(el.realNumber!double == -real.infinity);
1467         assert(el.realNumber!real   == -real.infinity);
1468 
1469         el.realNumber!real = -real.infinity;
1470         assert(el.realNumber!float  == -real.infinity);
1471         assert(el.realNumber!double == -real.infinity);
1472         assert(el.realNumber!real   == -real.infinity);
1473     }
1474 
1475     // Test NaN
1476     @system
1477     unittest
1478     {
1479         Element el = new Element();
1480 
1481         el.realNumber!float = float.nan;
1482         assert(el.realNumber!float.isNaN);
1483         assert(el.realNumber!double.isNaN);
1484         assert(el.realNumber!real.isNaN);
1485 
1486         el.realNumber!double = float.nan;
1487         assert(el.realNumber!float.isNaN);
1488         assert(el.realNumber!double.isNaN);
1489         assert(el.realNumber!real.isNaN);
1490 
1491         el.realNumber!real = float.nan;
1492         assert(el.realNumber!float.isNaN);
1493         assert(el.realNumber!double.isNaN);
1494         assert(el.realNumber!real.isNaN);
1495 
1496         el.realNumber!float = double.nan;
1497         assert(el.realNumber!float.isNaN);
1498         assert(el.realNumber!double.isNaN);
1499         assert(el.realNumber!real.isNaN);
1500 
1501         el.realNumber!double = double.nan;
1502         assert(el.realNumber!float.isNaN);
1503         assert(el.realNumber!double.isNaN);
1504         assert(el.realNumber!real.isNaN);
1505 
1506         el.realNumber!real = double.nan;
1507         assert(el.realNumber!float.isNaN);
1508         assert(el.realNumber!double.isNaN);
1509         assert(el.realNumber!real.isNaN);
1510 
1511         el.realNumber!float = real.nan;
1512         assert(el.realNumber!float.isNaN);
1513         assert(el.realNumber!double.isNaN);
1514         assert(el.realNumber!real.isNaN);
1515 
1516         el.realNumber!double = real.nan;
1517         assert(el.realNumber!float.isNaN);
1518         assert(el.realNumber!double.isNaN);
1519         assert(el.realNumber!real.isNaN);
1520 
1521         el.realNumber!real = real.nan;
1522         assert(el.realNumber!float.isNaN);
1523         assert(el.realNumber!double.isNaN);
1524         assert(el.realNumber!real.isNaN);
1525     }
1526 
1527     // Tests of maximum and minimums of integral types
1528     @system
1529     unittest
1530     {
1531         immutable float[] tests = cast(float[]) [
1532             byte.max,
1533             byte.min,
1534             ubyte.max,
1535             ubyte.min,
1536             short.max,
1537             short.min,
1538             ushort.max,
1539             ushort.min,
1540             int.max,
1541             int.min,
1542             uint.max,
1543             uint.min,
1544             long.max,
1545             long.min,
1546             ulong.max,
1547             ulong.min
1548         ];
1549 
1550         Element el = new Element();
1551 
1552         foreach (test; tests)
1553         {
1554             el.realNumber!float = cast(float) test;
1555             assert(approxEqual(el.realNumber!float, cast(float) test));
1556             el.realNumber!double = cast(double) test;
1557             assert(approxEqual(el.realNumber!double, cast(double) test));
1558             el.realNumber!real = test;
1559             assert(approxEqual(el.realNumber!real, test));
1560         }
1561     }
1562 
1563     // Tests of maximum and minimums of floating-point types
1564     @system
1565     unittest
1566     {
1567         Element el = new Element();
1568 
1569         // Maximums and minimums of floats
1570         el.realNumber!float = float.max;
1571         assert(approxEqual(el.realNumber!float, float.max));
1572         el.realNumber!double = cast(double) float.max;
1573         assert(approxEqual(el.realNumber!double, float.max));
1574         el.realNumber!real = cast(real) float.max;
1575         assert(approxEqual(el.realNumber!real, float.max));
1576 
1577         el.realNumber!float = float.max_10_exp;
1578         assert(approxEqual(el.realNumber!float, float.max_10_exp));
1579         el.realNumber!double = cast(double) float.max_10_exp;
1580         assert(approxEqual(el.realNumber!double, float.max_10_exp));
1581         el.realNumber!real = cast(real) float.max_10_exp;
1582         assert(approxEqual(el.realNumber!real, float.max_10_exp));
1583 
1584         el.realNumber!float = float.max_exp;
1585         assert(approxEqual(el.realNumber!float, float.max_exp));
1586         el.realNumber!double = cast(double) float.max_exp;
1587         assert(approxEqual(el.realNumber!double, float.max_exp));
1588         el.realNumber!real = cast(real) float.max_exp;
1589         assert(approxEqual(el.realNumber!real, float.max_exp));
1590 
1591         el.realNumber!float = float.min_10_exp;
1592         assert(approxEqual(el.realNumber!float, float.min_10_exp));
1593         el.realNumber!double = cast(double) float.min_10_exp;
1594         assert(approxEqual(el.realNumber!double, float.min_10_exp));
1595         el.realNumber!real = cast(real) float.min_10_exp;
1596         assert(approxEqual(el.realNumber!real, float.min_10_exp));
1597 
1598         el.realNumber!float = float.min_exp;
1599         assert(approxEqual(el.realNumber!float, float.min_exp));
1600         el.realNumber!double = cast(double) float.min_exp;
1601         assert(approxEqual(el.realNumber!double, float.min_exp));
1602         el.realNumber!real = cast(real) float.min_exp;
1603         assert(approxEqual(el.realNumber!real, float.min_exp));
1604 
1605         el.realNumber!float = float.min_normal;
1606         assert(approxEqual(el.realNumber!float, float.min_normal));
1607         el.realNumber!double = cast(double) float.min_normal;
1608         assert(approxEqual(el.realNumber!double, float.min_normal));
1609         el.realNumber!real = cast(real) float.min_normal;
1610         assert(approxEqual(el.realNumber!real, float.min_normal));
1611 
1612         // Maximums and minimums of doubles
1613         el.realNumber!double = cast(double) double.max;
1614         assert(approxEqual(el.realNumber!double, double.max));
1615         el.realNumber!real = cast(real) double.max;
1616         assert(approxEqual(el.realNumber!real, double.max));
1617 
1618         el.realNumber!double = cast(double) double.max_10_exp;
1619         assert(approxEqual(el.realNumber!double, double.max_10_exp));
1620         el.realNumber!real = cast(real) double.max_10_exp;
1621         assert(approxEqual(el.realNumber!real, double.max_10_exp));
1622 
1623         el.realNumber!double = cast(double) double.max_exp;
1624         assert(approxEqual(el.realNumber!double, double.max_exp));
1625         el.realNumber!real = cast(real) double.max_exp;
1626         assert(approxEqual(el.realNumber!real, double.max_exp));
1627 
1628         el.realNumber!double = cast(double) double.min_10_exp;
1629         assert(approxEqual(el.realNumber!double, double.min_10_exp));
1630         el.realNumber!real = cast(real) double.min_10_exp;
1631         assert(approxEqual(el.realNumber!real, double.min_10_exp));
1632 
1633         el.realNumber!double = cast(double) double.min_exp;
1634         assert(approxEqual(el.realNumber!double, double.min_exp));
1635         el.realNumber!real = cast(real) double.min_exp;
1636         assert(approxEqual(el.realNumber!real, double.min_exp));
1637 
1638         el.realNumber!double = cast(double) double.min_normal;
1639         assert(approxEqual(el.realNumber!double, double.min_normal));
1640         el.realNumber!real = cast(real) double.min_normal;
1641         assert(approxEqual(el.realNumber!real, double.min_normal));
1642 
1643         // Maximums and minimums of reals
1644         el.realNumber!real = cast(real) real.max;
1645         assert(approxEqual(el.realNumber!real, real.max));
1646 
1647         el.realNumber!real = cast(real) real.max_10_exp;
1648         assert(approxEqual(el.realNumber!real, real.max_10_exp));
1649 
1650         el.realNumber!real = cast(real) real.max_exp;
1651         assert(approxEqual(el.realNumber!real, real.max_exp));
1652 
1653         el.realNumber!real = cast(real) real.min_10_exp;
1654         assert(approxEqual(el.realNumber!real, real.min_10_exp));
1655 
1656         el.realNumber!real = cast(real) real.min_exp;
1657         assert(approxEqual(el.realNumber!real, real.min_exp));
1658 
1659         el.realNumber!real = cast(real) real.min_normal;
1660         assert(approxEqual(el.realNumber!real, real.min_normal));
1661     }
1662 
1663     // Test with all of the math constants, to make sure there are no edge cases.
1664     @system
1665     unittest
1666     {
1667         import std.math :
1668             E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, LN10, LN2, LOG2,
1669             LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2, sqrt;
1670 
1671         immutable real[] tests = [
1672             E, PI, PI_2, PI_4, M_1_PI, M_2_PI, M_2_SQRTPI, LN10, LN2, LOG2,
1673             LOG2E, LOG2T, LOG10E, SQRT2, SQRT1_2,
1674             (SQRT2 / 2.0), // SQRT_2_OVER_2
1675             ((1.0 + sqrt(5.0)) / 2.0), // GOLDEN_RATIO
1676             0.57721,      // EULER_MASCHERONI_CONSTANT
1677             0.2614972128, // MEISSEL_MERTENS_CONSTANT
1678             0.2801694990, // BERNSTEINS_CONSTANT
1679             0.3036630028, // GAUSS_KUZMIN_WIRSING_CONSTANT
1680             0.3532363718, // HAFNER_SARNAK_MCCURLEY_CONSTANT
1681             0.5671432904, // OMEGA_CONSTANT
1682             0.6243299885, // GOLOMB_DICKMAN_CONSTANT
1683             0.6434105462, // CAHENS_CONSTANT
1684             0.6601618158, // TWIN_PRIME_CONSTANT
1685             0.6627434193, // LAPLACE_LIMIT
1686             0.70258,      // LANDAU_RAMANUJAN_CONSTANT
1687             0.8093940205, // ALLADI_GRINSTEAD_CONSTANT
1688             0.87058838,   // BRUNS_CONSTANT_FOR_PRIME_QUADRUPLETS
1689             0.9159655941, // CATALANS_CONSTANT
1690             1.0986858055, // LENGYELS_CONSTANT
1691             1.13198824,   // VISWANATHS_CONSTANT
1692             1.2020569,    // APERYS_CONSTANT
1693             1.30357,      // CONWAYS_CONSTANT
1694             1.3063778838, // MILLS_CONSTANT
1695             1.3247179572, // PLASTIC_CONSTANT
1696             1.4513692348, // RAMANUJAN_SOLDNER_CONSTANT
1697             1.4560749485, // BACKHOUSES_CONSTANT
1698             1.4670780794, // PORTERS_CONSTANT
1699             1.5396007178, // LIEBS_SQUARE_ICE_CONSTANT
1700             1.6066951524, // ERDOS_BORWEIN_CONSTANT
1701             1.7052111401, // NIVENS_CONSTANT
1702             1.9021605831, // BRUNS_CONSTANT_FOR_TWIN_PRIMES
1703             2.2955871493, // UNIVERSAL_PARABOLIC_CONSTANT
1704             2.5029078750, // FEIGENBAUM_CONSTANT_ALPHA
1705             2.5849817595, // SIERPINSKIS_CONSTANT
1706             2.6854520010, // KHINCHINS_CONSTANT
1707             2.8077702420, // FRANSEN_ROBINSON_CONSTANT
1708             3.2758229187, // LEVYS_CONSTANT
1709             3.3598856662, // RECIPROCAL_FIBONACCI_CONSTANT
1710             4.6692016091, // FEIGENBAUM_CONSTANT_DELTA
1711             1.2824271291  // GLAISHER_KINKELIN_CONSTANT
1712         ];
1713 
1714         Element el = new Element();
1715 
1716         foreach (test; tests)
1717         {
1718             el.realNumber!float = cast(float) test;
1719             assert(approxEqual(el.realNumber!float, cast(float) test));
1720             el.realNumber!double = cast(double) test;
1721             assert(approxEqual(el.realNumber!double, cast(double) test));
1722             el.realNumber!real = test;
1723             assert(approxEqual(el.realNumber!real, test));
1724         }
1725     }
1726 
1727     /// Encodes an integer that represents an ENUMERATED value
1728     abstract public @property
1729     T enumerated(T)() const if (isIntegral!T && isSigned!T);
1730 
1731     /// Decodes an integer that represents an ENUMERATED value
1732     abstract public @property
1733     void enumerated(T)(in T value) if (isIntegral!T && isSigned!T);
1734 
1735     // Test all 255 signed 8-bit integers
1736     @system
1737     unittest
1738     {
1739         Element el = new Element();
1740         for (byte i = byte.min; i < byte.max; i++)
1741         {
1742             el.enumerated!byte = i;
1743             assert(el.enumerated!byte == i);
1744             el.enumerated!short = i;
1745             assert(el.enumerated!short == i);
1746             el.enumerated!int = i;
1747             assert(el.enumerated!int == i);
1748             el.enumerated!long = i;
1749             assert(el.enumerated!long == i);
1750         }
1751     }
1752 
1753     // Test all 65536 signed 16-bit integers
1754     @system
1755     unittest
1756     {
1757         Element el = new Element();
1758         for (short i = short.min; i < short.max; i++)
1759         {
1760             el.enumerated!short = i;
1761             assert(el.enumerated!short == i);
1762             el.enumerated!int = i;
1763             assert(el.enumerated!int == i);
1764             el.enumerated!long = i;
1765             assert(el.enumerated!long == i);
1766         }
1767     }
1768 
1769     // Test a few signed 32-bit integers
1770     @system
1771     unittest
1772     {
1773         Element el = new Element();
1774         for (int i = int.min; i < int.max-15_485_863; i += 15_485_863) // 15,485,863 is the millionth prime
1775         {
1776             ubyte[] ub;
1777             ub.length = int.sizeof;
1778             *cast(int *)&ub[0] = i;
1779             version (LittleEndian) reverse(ub);
1780             el.enumerated!int = i;
1781             assert(el.enumerated!int == i);
1782             el.enumerated!long = i;
1783             assert(el.enumerated!long == i);
1784         }
1785     }
1786 
1787     // Test a few signed 64-bit integers
1788     @system
1789     unittest
1790     {
1791         Element el = new Element();
1792         for (long i = long.min; i < long.max-965_211_250_482_432_409; i += 965_211_250_482_432_409) // 50 millionth prime^2
1793         {
1794             el.enumerated!long = i;
1795             assert(el.enumerated!long == i);
1796         }
1797     }
1798 
1799     @system
1800     unittest
1801     {
1802         Element el = new Element();
1803 
1804         // Tests for zero
1805         el.enumerated!byte = 0;
1806         assert(el.enumerated!byte == 0);
1807         assert(el.enumerated!short == 0);
1808         assert(el.enumerated!int == 0);
1809         assert(el.enumerated!long == 0L);
1810 
1811         el.enumerated!short = 0;
1812         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1813         assert(el.enumerated!short == 0);
1814         assert(el.enumerated!int == 0);
1815         assert(el.enumerated!long == 0L);
1816 
1817         el.enumerated!int = 0;
1818         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1819         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1820         assert(el.enumerated!int == 0);
1821         assert(el.enumerated!long == 0L);
1822 
1823         el.enumerated!long = 0L;
1824         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1825         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1826         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1827         assert(el.enumerated!long == 0L);
1828 
1829         // Tests for small positives
1830         el.enumerated!byte = 3;
1831         assert(el.enumerated!byte == 3);
1832         assert(el.enumerated!short == 3);
1833         assert(el.enumerated!int == 3);
1834         assert(el.enumerated!long == 3L);
1835 
1836         el.enumerated!short = 5;
1837         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1838         assert(el.enumerated!short == 5);
1839         assert(el.enumerated!int == 5);
1840         assert(el.enumerated!long == 5L);
1841 
1842         el.enumerated!int = 7;
1843         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1844         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1845         assert(el.enumerated!int == 7);
1846         assert(el.enumerated!long == 7L);
1847 
1848         el.enumerated!long = 9L;
1849         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1850         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1851         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1852         assert(el.enumerated!long == 9L);
1853 
1854         // Tests for small negatives
1855         el.enumerated!byte = -3;
1856         assert(el.enumerated!byte == -3);
1857         assert(el.enumerated!short == -3);
1858         assert(el.enumerated!int == -3);
1859         assert(el.enumerated!long == -3L);
1860 
1861         el.enumerated!short = -5;
1862         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1863         assert(el.enumerated!short == -5);
1864         assert(el.enumerated!int == -5);
1865         assert(el.enumerated!long == -5L);
1866 
1867         el.enumerated!int = -7;
1868         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1869         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1870         assert(el.enumerated!int == -7);
1871         assert(el.enumerated!long == -7L);
1872 
1873         el.enumerated!long = -9L;
1874         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1875         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1876         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1877         assert(el.enumerated!long == -9L);
1878 
1879         // Tests for large positives
1880         el.enumerated!short = 20000;
1881         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1882         assert(el.enumerated!short == 20000);
1883         assert(el.enumerated!int == 20000);
1884         assert(el.enumerated!long == 20000L);
1885 
1886         el.enumerated!int = 70000;
1887         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1888         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1889         assert(el.enumerated!int == 70000);
1890         assert(el.enumerated!long == 70000L);
1891 
1892         el.enumerated!long = 70000L;
1893         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1894         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1895         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1896         assert(el.enumerated!long == 70000L);
1897 
1898         // Tests for large negatives
1899         el.enumerated!short = -20000;
1900         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1901         assert(el.enumerated!short == -20000);
1902         assert(el.enumerated!int == -20000);
1903         assert(el.enumerated!long == -20000L);
1904 
1905         el.enumerated!int = -70000;
1906         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1907         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1908         assert(el.enumerated!int == -70000);
1909         assert(el.enumerated!long == -70000L);
1910 
1911         el.enumerated!long = -70000L;
1912         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1913         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1914         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1915         assert(el.enumerated!long == -70000L);
1916 
1917         // Tests for maximum values
1918         el.enumerated!byte = byte.max;
1919         assert(el.enumerated!byte == byte.max);
1920         assert(el.enumerated!short == byte.max);
1921         assert(el.enumerated!int == byte.max);
1922         assert(el.enumerated!long == byte.max);
1923 
1924         el.enumerated!short = short.max;
1925         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1926         assert(el.enumerated!short == short.max);
1927         assert(el.enumerated!int == short.max);
1928         assert(el.enumerated!long == short.max);
1929 
1930         el.enumerated!int = int.max;
1931         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1932         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1933         assert(el.enumerated!int == int.max);
1934         assert(el.enumerated!long == int.max);
1935 
1936         el.enumerated!long = long.max;
1937         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1938         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1939         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1940         assert(el.enumerated!long == long.max);
1941 
1942         // Tests for minimum values
1943         el.enumerated!byte = byte.min;
1944         assert(el.enumerated!byte == byte.min);
1945         assert(el.enumerated!short == byte.min);
1946         assert(el.enumerated!int == byte.min);
1947         assert(el.enumerated!long == byte.min);
1948 
1949         el.enumerated!short = short.min;
1950         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1951         assert(el.enumerated!short == short.min);
1952         assert(el.enumerated!int == short.min);
1953         assert(el.enumerated!long == short.min);
1954 
1955         el.enumerated!int = int.min;
1956         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1957         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1958         assert(el.enumerated!int == int.min);
1959         assert(el.enumerated!long == int.min);
1960 
1961         el.enumerated!long = long.min;
1962         // assertThrown!ASN1ValueSizeException(el.enumerated!byte);
1963         // assertThrown!ASN1ValueSizeException(el.enumerated!short);
1964         // assertThrown!ASN1ValueSizeException(el.enumerated!int);
1965         assert(el.enumerated!long == long.min);
1966 
1967         // Assert that accessor does not mutate state
1968         assert(el.enumerated!long == el.enumerated!long);
1969     }
1970 
1971     ///
1972     public alias embeddedPDV = embeddedPresentationDataValue;
1973     /**
1974         Decodes an $(MONO EmbeddedPDV), which is a constructed data type, defined in
1975         the $(LINK https://www.itu.int, International Telecommunications Union)'s
1976         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
1977 
1978         The specification defines $(MONO EmbeddedPDV) as:
1979 
1980         $(PRE
1981             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
1982                 identification CHOICE {
1983                     syntaxes SEQUENCE {
1984                         abstract OBJECT IDENTIFIER,
1985                         transfer OBJECT IDENTIFIER },
1986                     syntax OBJECT IDENTIFIER,
1987                     presentation-context-id INTEGER,
1988                     context-negotiation SEQUENCE {
1989                         presentation-context-id INTEGER,
1990                         transfer-syntax OBJECT IDENTIFIER },
1991                     transfer-syntax OBJECT IDENTIFIER,
1992                     fixed NULL },
1993                 data-value-descriptor ObjectDescriptor OPTIONAL,
1994                 data-value OCTET STRING }
1995             (WITH COMPONENTS { ... , data-value-descriptor ABSENT })
1996         )
1997 
1998         This assumes AUTOMATIC TAGS, so all of the identification choices
1999         will be context-specific and numbered from 0 to 5.
2000     */
2001     abstract public @property
2002     EmbeddedPDV embeddedPresentationDataValue() const;
2003 
2004     /**
2005         Encodes an $(MONO EmbeddedPDV), which is a constructed data type, defined in
2006         the $(LINK https://www.itu.int, International Telecommunications Union)'s
2007         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2008 
2009         The specification defines $(MONO EmbeddedPDV) as:
2010 
2011         $(PRE
2012             EmbeddedPDV ::= [UNIVERSAL 11] IMPLICIT SEQUENCE {
2013                 identification CHOICE {
2014                     syntaxes SEQUENCE {
2015                         abstract OBJECT IDENTIFIER,
2016                         transfer OBJECT IDENTIFIER },
2017                     syntax OBJECT IDENTIFIER,
2018                     presentation-context-id INTEGER,
2019                     context-negotiation SEQUENCE {
2020                         presentation-context-id INTEGER,
2021                         transfer-syntax OBJECT IDENTIFIER },
2022                     transfer-syntax OBJECT IDENTIFIER,
2023                     fixed NULL },
2024                 data-value-descriptor ObjectDescriptor OPTIONAL,
2025                 data-value OCTET STRING }
2026             (WITH COMPONENTS { ... , data-value-descriptor ABSENT })
2027         )
2028 
2029         This assumes AUTOMATIC TAGS, so all of the identification choices
2030         will be context-specific and numbered from 0 to 5.
2031     */
2032     abstract public @property
2033     void embeddedPresentationDataValue(in EmbeddedPDV value);
2034 
2035     @system
2036     unittest
2037     {
2038         ASN1Syntaxes syn = ASN1Syntaxes();
2039         syn.abstractSyntax = new OID(1, 3, 6, 4, 1, 256, 7);
2040         syn.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 8);
2041 
2042         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2043         id.syntaxes = syn;
2044 
2045         EmbeddedPDV input = EmbeddedPDV();
2046         input.identification = id;
2047         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2048 
2049         Element el = new Element();
2050         el.embeddedPDV = input;
2051         EmbeddedPDV output = el.embeddedPDV;
2052         assert(output.identification.syntaxes.abstractSyntax == new OID(1, 3, 6, 4, 1, 256, 7));
2053         assert(output.identification.syntaxes.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 8));
2054         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2055 
2056         // Assert that accessor does not mutate state
2057         assert(el.embeddedPDV == el.embeddedPDV);
2058     }
2059 
2060     @system
2061     unittest
2062     {
2063         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2064         id.syntax = new OID(1, 3, 6, 4, 1, 256, 39);
2065 
2066         EmbeddedPDV input = EmbeddedPDV();
2067         input.identification = id;
2068         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2069 
2070         Element el = new Element();
2071         el.embeddedPDV = input;
2072         EmbeddedPDV output = el.embeddedPDV;
2073         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
2074         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2075     }
2076 
2077     @system
2078     unittest
2079     {
2080         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2081         id.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 39);
2082 
2083         EmbeddedPDV input = EmbeddedPDV();
2084         input.identification = id;
2085         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2086 
2087         Element el = new Element();
2088         el.embeddedPDV = input;
2089         EmbeddedPDV output = el.embeddedPDV;
2090         assert(output.identification.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 39));
2091         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2092     }
2093 
2094     @system
2095     unittest
2096     {
2097         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2098         id.fixed = true;
2099 
2100         EmbeddedPDV input = EmbeddedPDV();
2101         input.identification = id;
2102         input.dataValue = [ 0x03u, 0x05u, 0x07u, 0x09u ];
2103 
2104         Element el = new Element();
2105         el.embeddedPDV = input;
2106         EmbeddedPDV output = el.embeddedPDV;
2107         assert(output.identification.fixed == true);
2108         assert(output.dataValue == [ 0x03u, 0x05u, 0x07u, 0x09u ]);
2109     }
2110 
2111     ///
2112     public alias utf8String = unicodeTransformationFormat8String;
2113     /// Decodes a UTF-8 String
2114     abstract public @property
2115     string unicodeTransformationFormat8String() const;
2116 
2117     /// Encodes a UTF-8 String
2118     abstract public @property
2119     void unicodeTransformationFormat8String(in string value);
2120 
2121     @system
2122     unittest
2123     {
2124         Element el = new Element();
2125         el.utf8String = "";
2126         assert(el.utf8String == "");
2127         el.utf8String = "henlo borthers";
2128         assert(el.utf8String == "henlo borthers");
2129 
2130         // Assert that accessor does not mutate state
2131         assert(el.utf8String == el.utf8String);
2132     }
2133 
2134     // Test that mutating the value does not mutate an external reference.
2135     @system
2136     unittest
2137     {
2138         string test = "HENLO";
2139         Element el = new Element();
2140         el.utf8String = test;
2141         el.value[4] = 0x88u;
2142         assert(test[4] == 'O');
2143     }
2144 
2145     ///
2146     public alias roid = relativeObjectIdentifier;
2147     ///
2148     public alias relativeOID = relativeObjectIdentifier;
2149     /// Decodes a portion of an Object Identifier
2150     abstract public @property
2151     OIDNode[] relativeObjectIdentifier() const;
2152 
2153     /// Encodes a porition of an Object Identifier
2154     abstract public @property
2155     void relativeObjectIdentifier(in OIDNode[] value);
2156 
2157     @system
2158     unittest
2159     {
2160         Element el = new Element();
2161         OIDNode[] input = [ OIDNode(3), OIDNode(5), OIDNode(7), OIDNode(9) ];
2162         el.roid = input;
2163         OIDNode[] output = el.roid;
2164 
2165         assert(input.length == output.length);
2166         for (ptrdiff_t i = 0; i < input.length; i++)
2167         {
2168             assert(input[i] == output[i]);
2169         }
2170 
2171         // Assert that accessor does not mutate state
2172         assert(el.relativeObjectIdentifier == el.relativeObjectIdentifier);
2173     }
2174 
2175     /**
2176         Decodes an array of elements.
2177 
2178         Credits:
2179             Thanks to StackOverflow user
2180             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2181             for teaching me how to create the abstract method that uses the
2182             child class as a template.
2183     */
2184     abstract public @property
2185     Element[] sequence() const;
2186 
2187     /**
2188         Encodes an array of elements.
2189 
2190         Credits:
2191             Thanks to StackOverflow user
2192             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2193             for teaching me how to create the abstract method that uses the
2194             child class as a template.
2195     */
2196     abstract public @property
2197     void sequence(in Element[] value);
2198 
2199     /**
2200         Decodes an array of elements.
2201 
2202         Credits:
2203             Thanks to StackOverflow user
2204             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2205             for teaching me how to create the abstract method that uses the
2206             child class as a template.
2207     */
2208     abstract public @property
2209     Element[] set() const;
2210 
2211     /**
2212         Encodes an array of elements.
2213 
2214         Credits:
2215             Thanks to StackOverflow user
2216             $(LINK https://stackoverflow.com/users/359297/biotronic, BioTronic)
2217             for teaching me how to create the abstract method that uses the
2218             child class as a template.
2219     */
2220     abstract public @property
2221     void set(in Element[] value);
2222 
2223     /**
2224         Decodes a string, where the characters of the string are limited to
2225         0 - 9 and space.
2226     */
2227     abstract public @property
2228     string numericString() const;
2229 
2230     /**
2231         Encodes a string, where the characters of the string are limited to
2232         0 - 9 and space.
2233     */
2234     abstract public @property
2235     void numericString(in string value);
2236 
2237     @system
2238     unittest
2239     {
2240         Element el = new Element();
2241         el.numericString = "";
2242         assert(el.numericString == "");
2243         el.numericString = "1234567890";
2244         assert(el.numericString == "1234567890");
2245         el.numericString = " ";
2246         assert(el.numericString == " ");
2247         assertThrown!ASN1ValueCharactersException(el.numericString = "hey hey");
2248         assertThrown!ASN1ValueCharactersException(el.numericString = "12345676789A");
2249 
2250         // Assert that accessor does not mutate state
2251         assert(el.numericString == el.numericString);
2252     }
2253 
2254     // Test that mutating the value does not mutate an external reference.
2255     @system
2256     unittest
2257     {
2258         string test = "12345";
2259         Element el = new Element();
2260         el.numericString = test;
2261         el.value[4] = 0x88u;
2262         assert(test[4] == '5');
2263     }
2264 
2265     /**
2266         Decodes a string that will only contain characters a-z, A-Z, 0-9,
2267         space, apostrophe, parentheses, comma, minus, plus, period,
2268         forward slash, colon, equals, and question mark.
2269     */
2270     abstract public @property
2271     string printableString() const;
2272 
2273     /**
2274         Encodes a string that will only contain characters a-z, A-Z, 0-9,
2275         space, apostrophe, parentheses, comma, minus, plus, period,
2276         forward slash, colon, equals, and question mark.
2277     */
2278     abstract public @property
2279     void printableString(in string value);
2280 
2281     @system
2282     unittest
2283     {
2284         Element el = new Element();
2285         el.printableString = "";
2286         assert(el.printableString == "");
2287         el.printableString = "1234567890 asdfjkl";
2288         assert(el.printableString == "1234567890 asdfjkl");
2289         el.printableString = " ";
2290         assert(el.printableString == " ");
2291         assertThrown!ASN1ValueCharactersException(el.printableString = "\t");
2292         assertThrown!ASN1ValueCharactersException(el.printableString = "\n");
2293         assertThrown!ASN1ValueCharactersException(el.printableString = "\0");
2294         assertThrown!ASN1ValueCharactersException(el.printableString = "\v");
2295         assertThrown!ASN1ValueCharactersException(el.printableString = "\b");
2296         assertThrown!ASN1ValueCharactersException(el.printableString = "\r");
2297         assertThrown!ASN1ValueCharactersException(el.printableString = "\x13");
2298 
2299         // Assert that accessor does not mutate state
2300         assert(el.printableString == el.printableString);
2301     }
2302 
2303     // Test that mutating the value does not mutate an external reference.
2304     @system
2305     unittest
2306     {
2307         string test = "HENLO";
2308         Element el = new Element();
2309         el.printableString = test;
2310         el.value[4] = 0x88u;
2311         assert(test[4] == 'O');
2312     }
2313 
2314     ///
2315     public alias t61String = teletexString;
2316     /// Decodes bytes representing the T.61 Character Set
2317     abstract public @property
2318     ubyte[] teletexString() const;
2319 
2320     /// Encodes bytes representing the T.61 Character Set
2321     abstract public @property
2322     void teletexString(in ubyte[] value);
2323 
2324     @system
2325     unittest
2326     {
2327         Element el = new Element();
2328         el.teletexString = [];
2329         assert(el.teletexString == []);
2330         el.teletexString = [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ];
2331         assert(el.teletexString == [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ]);
2332 
2333         // Assert that accessor does not mutate state
2334         assert(el.teletexString == el.teletexString);
2335     }
2336 
2337     // Test that mutating the value does not mutate an external reference.
2338     @system
2339     unittest
2340     {
2341         ubyte[] test = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
2342         Element el = new Element();
2343         el.teletexString = test;
2344         el.value[4] = 0x88u;
2345         assert(test[4] == 0x6Au);
2346     }
2347 
2348     // Test that mutating a large value does not mutate an external reference.
2349     @system
2350     unittest
2351     {
2352         ubyte[] test;
2353         test.length = 10000u;
2354         Element el = new Element();
2355         el.teletexString = test;
2356         el.value[4] = 0x88u;
2357         assert(test[4] == 0x00u);
2358     }
2359 
2360     abstract public @property
2361     ubyte[] videotexString() const;
2362 
2363     abstract public @property
2364     void videotexString(in ubyte[] value);
2365 
2366     @system
2367     unittest
2368     {
2369         Element el = new Element();
2370         el.videotexString = [];
2371         assert(el.videotexString == []);
2372         el.videotexString = [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ];
2373         assert(el.videotexString == [ 0x01u, 0x03u, 0x05u, 0x07u, 0x09u ]);
2374 
2375         // Assert that accessor does not mutate state
2376         assert(el.videotexString == el.videotexString);
2377     }
2378 
2379     // Test that mutating the value does not mutate an external reference.
2380     @system
2381     unittest
2382     {
2383         ubyte[] test = [ 0x05u, 0x02u, 0xFFu, 0x00u, 0x6Au ];
2384         Element el = new Element();
2385         el.videotexString = test;
2386         el.value[4] = 0x88u;
2387         assert(test[4] == 0x6Au);
2388     }
2389 
2390     // Test that mutating a large value does not mutate an external reference.
2391     @system
2392     unittest
2393     {
2394         ubyte[] test;
2395         test.length = 10000u;
2396         Element el = new Element();
2397         el.videotexString = test;
2398         el.value[4] = 0x88u;
2399         assert(test[4] == 0x00u);
2400     }
2401 
2402     ///
2403     public alias ia5String = internationalAlphabetNumber5String;
2404     /// Decodes a string of ASCII characters
2405     abstract public @property
2406     string internationalAlphabetNumber5String() const;
2407 
2408     /// Encodes a string of ASCII characters
2409     abstract public @property
2410     void internationalAlphabetNumber5String(in string value);
2411 
2412     @system
2413     unittest
2414     {
2415         Element el = new Element();
2416         el.ia5String = "";
2417         assert(el.ia5String == "");
2418         el.ia5String = "Nitro dubs & T-Rix";
2419         assert(el.ia5String == "Nitro dubs & T-Rix");
2420         assertThrown!ASN1ValueCharactersException(el.ia5String = "Nitro dubs \xD7 T-Rix");
2421 
2422         // Assert that accessor does not mutate state
2423         assert(el.ia5String == el.ia5String);
2424     }
2425 
2426     // Test that mutating the value does not mutate an external reference.
2427     @system
2428     unittest
2429     {
2430         string test = "HENLO";
2431         Element el = new Element();
2432         el.ia5String = test;
2433         el.value[4] = 0x88u;
2434         assert(test[4] == 'O');
2435     }
2436 
2437     ///
2438     public alias utc = coordinatedUniversalTime;
2439     ///
2440     public alias utcTime = coordinatedUniversalTime;
2441     /// Decodes a DateTime
2442     abstract public @property
2443     DateTime coordinatedUniversalTime() const;
2444 
2445     /// Encodes a DateTime
2446     abstract public @property
2447     void coordinatedUniversalTime(in DateTime value);
2448 
2449     @system
2450     unittest
2451     {
2452         Element el = new Element();
2453         el.utcTime = DateTime(2017, 10, 3);
2454         assert(el.utcTime == DateTime(2017, 10, 3));
2455 
2456         // Assert that accessor does not mutate state
2457         assert(el.utcTime == el.utcTime);
2458     }
2459 
2460     /// Decodes a DateTime
2461     abstract public @property
2462     DateTime generalizedTime() const;
2463 
2464     /// Encodes a DateTime
2465     abstract public @property
2466     void generalizedTime(in DateTime value);
2467 
2468     @system
2469     unittest
2470     {
2471         Element el = new Element();
2472         el.generalizedTime = DateTime(2017, 10, 3);
2473         assert(el.generalizedTime == DateTime(2017, 10, 3));
2474 
2475         // Assert that accessor does not mutate state
2476         assert(el.generalizedTime == el.generalizedTime);
2477     }
2478 
2479     /**
2480         Decodes an ASCII string that contains only characters between and
2481         including 0x20 and 0x75.
2482 
2483         Sources:
2484             $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF, ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
2485             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022)
2486             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
2487 
2488     */
2489     deprecated
2490     abstract public @property
2491     string graphicString() const;
2492 
2493     /**
2494         Encodes an ASCII string that contains only characters between and
2495         including 0x20 and 0x75.
2496 
2497         Sources:
2498             $(LINK http://www.oss.com/asn1/resources/books-whitepapers-pubs/dubuisson-asn1-book.PDF, ASN.1: Communication Between Heterogeneous Systems, pages 175-178)
2499             $(LINK https://en.wikipedia.org/wiki/ISO/IEC_2022, The Wikipedia Page on ISO 2022)
2500             $(LINK https://www.iso.org/standard/22747.html, ISO 2022)
2501 
2502     */
2503     deprecated
2504     abstract public @property
2505     void graphicString(in string value);
2506 
2507     @system
2508     unittest
2509     {
2510         Element el = new Element();
2511         el.graphicString = "";
2512         assert(el.graphicString == "");
2513         el.graphicString = "Nitro dubs & T-Rix";
2514         assert(el.graphicString == "Nitro dubs & T-Rix");
2515         el.graphicString = " ";
2516         assert(el.graphicString == " ");
2517         assertThrown!ASN1ValueCharactersException(el.graphicString = "\xD7");
2518         assertThrown!ASN1ValueCharactersException(el.graphicString = "\t");
2519         assertThrown!ASN1ValueCharactersException(el.graphicString = "\r");
2520         assertThrown!ASN1ValueCharactersException(el.graphicString = "\n");
2521         assertThrown!ASN1ValueCharactersException(el.graphicString = "\b");
2522         assertThrown!ASN1ValueCharactersException(el.graphicString = "\v");
2523         assertThrown!ASN1ValueCharactersException(el.graphicString = "\f");
2524         assertThrown!ASN1ValueCharactersException(el.graphicString = "\0");
2525 
2526         // Assert that accessor does not mutate state
2527         assert(el.graphicString == el.graphicString);
2528     }
2529 
2530     // Test that mutating the value does not mutate an external reference.
2531     @system
2532     unittest
2533     {
2534         string test = "HENLO";
2535         Element el = new Element();
2536         el.graphicString = test;
2537         el.value[4] = 0x88u;
2538         assert(test[4] == 'O');
2539     }
2540 
2541     ///
2542     public alias iso646String = visibleString;
2543     /**
2544         Decodes a string that only contains characters between and including
2545         0x20 and 0x7E. (Honestly, I don't know how this differs from
2546         GraphicalString.)
2547     */
2548     abstract public @property
2549     string visibleString() const;
2550 
2551     /**
2552         Encodes a string that only contains characters between and including
2553         0x20 and 0x7E. (Honestly, I don't know how this differs from
2554         GraphicalString.)
2555     */
2556     abstract public @property
2557     void visibleString(in string value);
2558 
2559     @system
2560     unittest
2561     {
2562         Element el = new Element();
2563         el.visibleString = "";
2564         assert(el.visibleString == "");
2565         el.visibleString = "hey hey";
2566         assert(el.visibleString == "hey hey");
2567         el.visibleString = " ";
2568         assert(el.visibleString == " ");
2569         assertThrown!ASN1ValueCharactersException(el.visibleString = "\xD7");
2570         assertThrown!ASN1ValueCharactersException(el.visibleString = "\t");
2571         assertThrown!ASN1ValueCharactersException(el.visibleString = "\r");
2572         assertThrown!ASN1ValueCharactersException(el.visibleString = "\n");
2573         assertThrown!ASN1ValueCharactersException(el.visibleString = "\b");
2574         assertThrown!ASN1ValueCharactersException(el.visibleString = "\v");
2575         assertThrown!ASN1ValueCharactersException(el.visibleString = "\f");
2576         assertThrown!ASN1ValueCharactersException(el.visibleString = "\0");
2577 
2578         // Assert that accessor does not mutate state
2579         assert(el.visibleString == el.visibleString);
2580     }
2581 
2582     // Test that mutating the value does not mutate an external reference.
2583     @system
2584     unittest
2585     {
2586         string test = "HENLO";
2587         Element el = new Element();
2588         el.visibleString = test;
2589         el.value[4] = 0x88u;
2590         assert(test[4] == 'O');
2591     }
2592 
2593     /// Decodes a string containing only ASCII characters.
2594     deprecated
2595     abstract public @property
2596     string generalString();
2597 
2598     /// Encodes a string containing only ASCII characters.
2599     deprecated
2600     abstract public @property
2601     void generalString(in string value);
2602 
2603     @system
2604     unittest
2605     {
2606         Element el = new Element();
2607         el.generalString = "";
2608         assert(el.generalString == "");
2609         el.generalString = "foin-ass sweatpants from BUCCI \0\n\t\b\v\r\f";
2610         assert(el.generalString == "foin-ass sweatpants from BUCCI \0\n\t\b\v\r\f");
2611         assertThrown!ASN1ValueCharactersException(el.generalString = "\xF5");
2612 
2613         // Assert that accessor does not mutate state
2614         assert(el.generalString == el.generalString);
2615     }
2616 
2617     // Test that mutating the value does not mutate an external reference.
2618     @system
2619     unittest
2620     {
2621         string test = "HENLO";
2622         Element el = new Element();
2623         el.generalString = test;
2624         el.value[4] = 0x88u;
2625         assert(test[4] == 'O');
2626     }
2627 
2628     /// Decodes a string of UTF-32 characters
2629     abstract public @property
2630     dstring universalString() const;
2631 
2632     /// Encodes a string of UTF-32 characters
2633     abstract public @property
2634     void universalString(in dstring value);
2635 
2636     @system
2637     unittest
2638     {
2639         Element el = new Element();
2640         el.universalString = ""d;
2641         assert(el.universalString == ""d);
2642         el.universalString = "abcd"d;
2643         assert(el.universalString == "abcd"d);
2644 
2645         // Assert that accessor does not mutate state
2646         assert(el.universalString == el.universalString);
2647     }
2648 
2649     // Test that mutating the value does not mutate an external reference.
2650     @system
2651     unittest
2652     {
2653         dstring test = "HENLO"d;
2654         Element el = new Element();
2655         el.universalString = test;
2656         el.value[4] = 0x88u;
2657         assert(test[4] == 'O');
2658     }
2659 
2660     /**
2661         Decodes a $(MONO CHARACTER STRING), which is a constructed data type, defined
2662         in the $(LINK https://www.itu.int, International Telecommunications Union)'s
2663         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2664 
2665         The specification defines $(MONO CHARACTER STRING) as:
2666 
2667         $(PRE
2668             CHARACTER STRING ::= [UNIVERSAL 29] SEQUENCE {
2669                 identification CHOICE {
2670                     syntaxes SEQUENCE {
2671                         abstract OBJECT IDENTIFIER,
2672                         transfer OBJECT IDENTIFIER },
2673                     syntax OBJECT IDENTIFIER,
2674                     presentation-context-id INTEGER,
2675                     context-negotiation SEQUENCE {
2676                         presentation-context-id INTEGER,
2677                         transfer-syntax OBJECT IDENTIFIER },
2678                     transfer-syntax OBJECT IDENTIFIER,
2679                     fixed NULL },
2680                 string-value OCTET STRING }
2681         )
2682 
2683         This assumes AUTOMATIC TAGS, so all of the identification choices
2684         will be context-specific and numbered from 0 to 5.
2685     */
2686     abstract public @property
2687     CharacterString characterString() const;
2688 
2689     /**
2690         Encodes a $(MONO CHARACTER STRING), which is a constructed data type, defined
2691         in the $(LINK https://www.itu.int, International Telecommunications Union)'s
2692         $(LINK https://www.itu.int/rec/T-REC-X.680/en, X.680).
2693 
2694         The specification defines $(MONO CHARACTER STRING) as:
2695 
2696         $(PRE
2697             CHARACTER STRING ::= [UNIVERSAL 29] SEQUENCE {
2698                 identification CHOICE {
2699                     syntaxes SEQUENCE {
2700                         abstract OBJECT IDENTIFIER,
2701                         transfer OBJECT IDENTIFIER },
2702                     syntax OBJECT IDENTIFIER,
2703                     presentation-context-id INTEGER,
2704                     context-negotiation SEQUENCE {
2705                         presentation-context-id INTEGER,
2706                         transfer-syntax OBJECT IDENTIFIER },
2707                     transfer-syntax OBJECT IDENTIFIER,
2708                     fixed NULL },
2709                 string-value OCTET STRING }
2710         )
2711 
2712         This assumes AUTOMATIC TAGS, so all of the identification choices
2713         will be context-specific and numbered from 0 to 5.
2714     */
2715     abstract public @property
2716     void characterString(in CharacterString value);
2717 
2718     @system
2719     unittest
2720     {
2721         ASN1Syntaxes syn = ASN1Syntaxes();
2722         syn.abstractSyntax = new OID(1, 3, 6, 4, 1, 256, 7);
2723         syn.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 8);
2724 
2725         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2726         id.syntaxes = syn;
2727 
2728         CharacterString input = CharacterString();
2729         input.identification = id;
2730         input.stringValue = [ 'H', 'E', 'L', 'N', 'O' ];
2731 
2732         Element el = new Element();
2733         el.characterString = input;
2734 
2735         CharacterString output = el.characterString;
2736         assert(output.identification.syntaxes.abstractSyntax == new OID(1, 3, 6, 4, 1, 256, 7));
2737         assert(output.identification.syntaxes.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 8));
2738         assert(output.stringValue == [ 'H', 'E', 'L', 'N', 'O' ]);
2739     }
2740 
2741     @system
2742     unittest
2743     {
2744         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2745         id.syntax = new OID(1, 3, 6, 4, 1, 256, 39);
2746 
2747         CharacterString input = CharacterString();
2748         input.identification = id;
2749         input.stringValue = [ 'H', 'E', 'N', 'L', 'O' ];
2750 
2751         Element el = new Element();
2752         el.characterString = input;
2753         CharacterString output = el.characterString;
2754         assert(output.identification.syntax == new OID(1, 3, 6, 4, 1, 256, 39));
2755         assert(output.stringValue == [ 'H', 'E', 'N', 'L', 'O' ]);
2756     }
2757 
2758     @system
2759     unittest
2760     {
2761         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2762         id.transferSyntax = new OID(1, 3, 6, 4, 1, 256, 39);
2763 
2764         CharacterString input = CharacterString();
2765         input.identification = id;
2766         input.stringValue = [ 'H', 'E', 'N', 'L', 'O' ];
2767 
2768         Element el = new Element();
2769         el.characterString = input;
2770         CharacterString output = el.characterString;
2771         assert(output.identification.transferSyntax == new OID(1, 3, 6, 4, 1, 256, 39));
2772         assert(output.stringValue == [ 'H', 'E', 'N', 'L', 'O' ]);
2773     }
2774 
2775     @system
2776     unittest
2777     {
2778         ASN1ContextSwitchingTypeID id = ASN1ContextSwitchingTypeID();
2779         id.fixed = true;
2780 
2781         CharacterString input = CharacterString();
2782         input.identification = id;
2783         input.stringValue = [ 'H', 'E', 'N', 'L', 'O' ];
2784 
2785         Element el = new Element();
2786         el.characterString = input;
2787         CharacterString output = el.characterString;
2788         assert(output.identification.fixed == true);
2789         assert(output.stringValue == [ 'H', 'E', 'N', 'L', 'O' ]);
2790 
2791         // Assert that accessor does not mutate state
2792         assert(el.characterString == el.characterString);
2793     }
2794 
2795     ///
2796     public alias bmpString = basicMultilingualPlaneString;
2797     /// Decodes a string of UTF-16 characters
2798     abstract public @property
2799     wstring basicMultilingualPlaneString() const;
2800 
2801     /// Encodes a string of UTF-16 characters
2802     abstract public @property
2803     void basicMultilingualPlaneString(in wstring value);
2804 
2805     @system
2806     unittest
2807     {
2808         Element el = new Element();
2809         el.bmpString = ""w;
2810         assert(el.bmpString == ""w);
2811         el.bmpString = "abcd"w;
2812         assert(el.bmpString == "abcd"w);
2813 
2814         // Assert that accessor does not mutate state
2815         assert(el.bmpString == el.bmpString);
2816     }
2817 
2818     // Test that mutating the value does not mutate an external reference.
2819     @system
2820     unittest
2821     {
2822         wstring test = "HENLO"w;
2823         Element el = new Element();
2824         el.bmpString = test;
2825         el.value[4] = 0x88u;
2826         assert(test[4] == 'O');
2827     }
2828 
2829 }