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 }