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