1 // ==++== 2 // 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // 5 // ==--== 6 // 7 // Don't override IsAlwaysNormalized because it is just a Unicode Transformation and could be confused. 8 // 9 10 namespace System.Text 11 { 12 using System; 13 using System.Runtime.Serialization; 14 using System.Security.Permissions; 15 using System.Diagnostics.Contracts; 16 17 18 [Serializable] 19 [System.Runtime.InteropServices.ComVisible(true)] 20 public class UTF7Encoding : Encoding 21 { 22 private const String base64Chars = 23 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 24 // 0123456789111111111122222222223333333333444444444455555555556666 25 // 012345678901234567890123456789012345678901234567890123 26 27 // These are the characters that can be directly encoded in UTF7. 28 private const String directChars = 29 "\t\n\r '(),-./0123456789:?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 30 31 // These are the characters that can be optionally directly encoded in UTF7. 32 private const String optionalChars = 33 "!\"#$%&*;<=>@[]^_`{|}"; 34 35 // The set of base 64 characters. 36 private byte[] base64Bytes; 37 // The decoded bits for every base64 values. This array has a size of 128 elements. 38 // The index is the code point value of the base 64 characters. The value is -1 if 39 // the code point is not a valid base 64 character. Otherwise, the value is a value 40 // from 0 ~ 63. 41 private sbyte[] base64Values; 42 // The array to decide if a Unicode code point below 0x80 can be directly encoded in UTF7. 43 // This array has a size of 128. 44 private bool[] directEncode; 45 46 [OptionalField(VersionAdded = 2)] 47 private bool m_allowOptionals; 48 49 private const int UTF7_CODEPAGE=65000; 50 51 UTF7Encoding()52 public UTF7Encoding() 53 : this(false) 54 { 55 } 56 UTF7Encoding(bool allowOptionals)57 public UTF7Encoding(bool allowOptionals) 58 : base(UTF7_CODEPAGE) //Set the data item. 59 { 60 // Allowing optionals? 61 this.m_allowOptionals = allowOptionals; 62 63 // Make our tables 64 MakeTables(); 65 } 66 MakeTables()67 private void MakeTables() 68 { 69 // Build our tables 70 base64Bytes = new byte[64]; 71 for (int i = 0; i < 64; i++) base64Bytes[i] = (byte)base64Chars[i]; 72 base64Values = new sbyte[128]; 73 for (int i = 0; i < 128; i++) base64Values[i] = -1; 74 for (int i = 0; i < 64; i++) base64Values[base64Bytes[i]] = (sbyte)i; 75 directEncode = new bool[128]; 76 int count = directChars.Length; 77 for (int i = 0; i < count; i++) 78 { 79 directEncode[directChars[i]] = true; 80 } 81 82 if (this.m_allowOptionals) 83 { 84 count = optionalChars.Length; 85 for (int i = 0; i < count; i++) 86 { 87 directEncode[optionalChars[i]] = true; 88 } 89 } 90 } 91 92 // We go ahead and set this because Encoding expects it, however nothing can fall back in UTF7. SetDefaultFallbacks()93 internal override void SetDefaultFallbacks() 94 { 95 // UTF7 had an odd decoderFallback behavior, and the Encoder fallback 96 // is irrelevent because we encode surrogates individually and never check for unmatched ones 97 // (so nothing can fallback during encoding) 98 this.encoderFallback = new EncoderReplacementFallback(String.Empty); 99 this.decoderFallback = new DecoderUTF7Fallback(); 100 } 101 102 103 #region Serialization 104 [OnDeserializing] OnDeserializing(StreamingContext ctx)105 private void OnDeserializing(StreamingContext ctx) 106 { 107 // make sure the optional fields initialized correctly. 108 base.OnDeserializing(); 109 } 110 111 [OnDeserialized] OnDeserialized(StreamingContext ctx)112 private void OnDeserialized(StreamingContext ctx) 113 { 114 base.OnDeserialized(); 115 116 if (m_deserializedFromEverett) 117 { 118 // If 1st optional char is encoded we're allowing optionals 119 m_allowOptionals = directEncode[optionalChars[0]]; 120 } 121 122 MakeTables(); 123 } 124 #endregion Serialization 125 126 127 128 [System.Runtime.InteropServices.ComVisible(false)] Equals(Object value)129 public override bool Equals(Object value) 130 { 131 UTF7Encoding that = value as UTF7Encoding; 132 if (that != null) 133 { 134 return (m_allowOptionals == that.m_allowOptionals) && 135 (EncoderFallback.Equals(that.EncoderFallback)) && 136 (DecoderFallback.Equals(that.DecoderFallback)); 137 } 138 return (false); 139 } 140 141 // Compared to all the other encodings, variations of UTF7 are unlikely 142 143 [System.Runtime.InteropServices.ComVisible(false)] GetHashCode()144 public override int GetHashCode() 145 { 146 return this.CodePage + this.EncoderFallback.GetHashCode() + this.DecoderFallback.GetHashCode(); 147 } 148 149 // 150 // The following methods are copied from EncodingNLS.cs. 151 // Unfortunately EncodingNLS.cs is internal and we're public, so we have to reimpliment them here. 152 // These should be kept in sync for the following classes: 153 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 154 // 155 156 // Returns the number of bytes required to encode a range of characters in 157 // a character array. 158 // 159 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 160 // So if you fix this, fix the others. Currently those include: 161 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 162 // parent method is safe 163 164 [System.Security.SecuritySafeCritical] // auto-generated GetByteCount(char[] chars, int index, int count)165 public override unsafe int GetByteCount(char[] chars, int index, int count) 166 { 167 // Validate input parameters 168 if (chars == null) 169 throw new ArgumentNullException("chars", 170 Environment.GetResourceString("ArgumentNull_Array")); 171 172 if (index < 0 || count < 0) 173 throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), 174 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 175 176 if (chars.Length - index < count) 177 throw new ArgumentOutOfRangeException("chars", 178 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); 179 Contract.EndContractBlock(); 180 181 // If no input, return 0, avoid fixed empty array problem 182 if (chars.Length == 0) 183 return 0; 184 185 // Just call the pointer version 186 fixed (char* pChars = chars) 187 return GetByteCount(pChars + index, count, null); 188 } 189 190 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 191 // So if you fix this, fix the others. Currently those include: 192 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 193 // parent method is safe 194 195 [System.Security.SecuritySafeCritical] // auto-generated 196 [System.Runtime.InteropServices.ComVisible(false)] GetByteCount(String s)197 public override unsafe int GetByteCount(String s) 198 { 199 // Validate input 200 if (s==null) 201 throw new ArgumentNullException("s"); 202 Contract.EndContractBlock(); 203 204 fixed (char* pChars = s) 205 return GetByteCount(pChars, s.Length, null); 206 } 207 208 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 209 // So if you fix this, fix the others. Currently those include: 210 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 211 212 [System.Security.SecurityCritical] // auto-generated 213 [CLSCompliant(false)] 214 [System.Runtime.InteropServices.ComVisible(false)] GetByteCount(char* chars, int count)215 public override unsafe int GetByteCount(char* chars, int count) 216 { 217 // Validate Parameters 218 if (chars == null) 219 throw new ArgumentNullException("chars", 220 Environment.GetResourceString("ArgumentNull_Array")); 221 222 if (count < 0) 223 throw new ArgumentOutOfRangeException("count", 224 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 225 Contract.EndContractBlock(); 226 227 // Call it with empty encoder 228 return GetByteCount(chars, count, null); 229 } 230 231 // Parent method is safe. 232 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 233 // So if you fix this, fix the others. Currently those include: 234 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 235 236 [System.Security.SecuritySafeCritical] // auto-generated 237 [System.Runtime.InteropServices.ComVisible(false)] GetBytes(String s, int charIndex, int charCount, byte[] bytes, int byteIndex)238 public override unsafe int GetBytes(String s, int charIndex, int charCount, 239 byte[] bytes, int byteIndex) 240 { 241 if (s == null || bytes == null) 242 throw new ArgumentNullException((s == null ? "s" : "bytes"), 243 Environment.GetResourceString("ArgumentNull_Array")); 244 245 if (charIndex < 0 || charCount < 0) 246 throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"), 247 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 248 249 if (s.Length - charIndex < charCount) 250 throw new ArgumentOutOfRangeException("s", 251 Environment.GetResourceString("ArgumentOutOfRange_IndexCount")); 252 253 if (byteIndex < 0 || byteIndex > bytes.Length) 254 throw new ArgumentOutOfRangeException("byteIndex", 255 Environment.GetResourceString("ArgumentOutOfRange_Index")); 256 Contract.EndContractBlock(); 257 258 int byteCount = bytes.Length - byteIndex; 259 260 // Fixed doesn't like empty arrays 261 if (bytes.Length == 0) 262 bytes = new byte[1]; 263 264 fixed (char* pChars = s) 265 fixed ( byte* pBytes = bytes) 266 return GetBytes(pChars + charIndex, charCount, 267 pBytes + byteIndex, byteCount, null); 268 } 269 270 // Encodes a range of characters in a character array into a range of bytes 271 // in a byte array. An exception occurs if the byte array is not large 272 // enough to hold the complete encoding of the characters. The 273 // GetByteCount method can be used to determine the exact number of 274 // bytes that will be produced for a given range of characters. 275 // Alternatively, the GetMaxByteCount method can be used to 276 // determine the maximum number of bytes that will be produced for a given 277 // number of characters, regardless of the actual character values. 278 // 279 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 280 // So if you fix this, fix the others. Currently those include: 281 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 282 // parent method is safe 283 284 [System.Security.SecuritySafeCritical] // auto-generated GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)285 public override unsafe int GetBytes(char[] chars, int charIndex, int charCount, 286 byte[] bytes, int byteIndex) 287 { 288 // Validate parameters 289 if (chars == null || bytes == null) 290 throw new ArgumentNullException((chars == null ? "chars" : "bytes"), 291 Environment.GetResourceString("ArgumentNull_Array")); 292 293 if (charIndex < 0 || charCount < 0) 294 throw new ArgumentOutOfRangeException((charIndex<0 ? "charIndex" : "charCount"), 295 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 296 297 if (chars.Length - charIndex < charCount) 298 throw new ArgumentOutOfRangeException("chars", 299 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); 300 301 if (byteIndex < 0 || byteIndex > bytes.Length) 302 throw new ArgumentOutOfRangeException("byteIndex", 303 Environment.GetResourceString("ArgumentOutOfRange_Index")); 304 Contract.EndContractBlock(); 305 306 // If nothing to encode return 0, avoid fixed problem 307 if (chars.Length == 0) 308 return 0; 309 310 // Just call pointer version 311 int byteCount = bytes.Length - byteIndex; 312 313 // Fixed doesn't like empty arrays 314 if (bytes.Length == 0) 315 bytes = new byte[1]; 316 317 fixed (char* pChars = chars) 318 fixed (byte* pBytes = bytes) 319 // Remember that byteCount is # to decode, not size of array. 320 return GetBytes(pChars + charIndex, charCount, 321 pBytes + byteIndex, byteCount, null); 322 } 323 324 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 325 // So if you fix this, fix the others. Currently those include: 326 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 327 328 [System.Security.SecurityCritical] // auto-generated 329 [CLSCompliant(false)] 330 [System.Runtime.InteropServices.ComVisible(false)] GetBytes(char* chars, int charCount, byte* bytes, int byteCount)331 public override unsafe int GetBytes(char* chars, int charCount, byte* bytes, int byteCount) 332 { 333 // Validate Parameters 334 if (bytes == null || chars == null) 335 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", 336 Environment.GetResourceString("ArgumentNull_Array")); 337 338 if (charCount < 0 || byteCount < 0) 339 throw new ArgumentOutOfRangeException((charCount<0 ? "charCount" : "byteCount"), 340 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 341 Contract.EndContractBlock(); 342 343 return GetBytes(chars, charCount, bytes, byteCount, null); 344 } 345 346 // Returns the number of characters produced by decoding a range of bytes 347 // in a byte array. 348 // 349 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 350 // So if you fix this, fix the others. Currently those include: 351 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 352 // parent method is safe 353 354 [System.Security.SecuritySafeCritical] // auto-generated GetCharCount(byte[] bytes, int index, int count)355 public override unsafe int GetCharCount(byte[] bytes, int index, int count) 356 { 357 // Validate Parameters 358 if (bytes == null) 359 throw new ArgumentNullException("bytes", 360 Environment.GetResourceString("ArgumentNull_Array")); 361 362 if (index < 0 || count < 0) 363 throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), 364 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 365 366 if (bytes.Length - index < count) 367 throw new ArgumentOutOfRangeException("bytes", 368 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); 369 Contract.EndContractBlock(); 370 371 // If no input just return 0, fixed doesn't like 0 length arrays. 372 if (bytes.Length == 0) 373 return 0; 374 375 // Just call pointer version 376 fixed (byte* pBytes = bytes) 377 return GetCharCount(pBytes + index, count, null); 378 } 379 380 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 381 // So if you fix this, fix the others. Currently those include: 382 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 383 384 [System.Security.SecurityCritical] // auto-generated 385 [CLSCompliant(false)] 386 [System.Runtime.InteropServices.ComVisible(false)] GetCharCount(byte* bytes, int count)387 public override unsafe int GetCharCount(byte* bytes, int count) 388 { 389 // Validate Parameters 390 if (bytes == null) 391 throw new ArgumentNullException("bytes", 392 Environment.GetResourceString("ArgumentNull_Array")); 393 394 if (count < 0) 395 throw new ArgumentOutOfRangeException("count", 396 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 397 Contract.EndContractBlock(); 398 399 return GetCharCount(bytes, count, null); 400 } 401 402 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 403 // So if you fix this, fix the others. Currently those include: 404 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 405 // parent method is safe 406 407 [System.Security.SecuritySafeCritical] // auto-generated GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)408 public override unsafe int GetChars(byte[] bytes, int byteIndex, int byteCount, 409 char[] chars, int charIndex) 410 { 411 // Validate Parameters 412 if (bytes == null || chars == null) 413 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", 414 Environment.GetResourceString("ArgumentNull_Array")); 415 416 if (byteIndex < 0 || byteCount < 0) 417 throw new ArgumentOutOfRangeException((byteIndex<0 ? "byteIndex" : "byteCount"), 418 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 419 420 if ( bytes.Length - byteIndex < byteCount) 421 throw new ArgumentOutOfRangeException("bytes", 422 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); 423 424 if (charIndex < 0 || charIndex > chars.Length) 425 throw new ArgumentOutOfRangeException("charIndex", 426 Environment.GetResourceString("ArgumentOutOfRange_Index")); 427 Contract.EndContractBlock(); 428 429 // If no input, return 0 & avoid fixed problem 430 if (bytes.Length == 0) 431 return 0; 432 433 // Just call pointer version 434 int charCount = chars.Length - charIndex; 435 436 // Fixed doesn't like empty arrays 437 if (chars.Length == 0) 438 chars = new char[1]; 439 440 fixed (byte* pBytes = bytes) 441 fixed (char* pChars = chars) 442 // Remember that charCount is # to decode, not size of array 443 return GetChars(pBytes + byteIndex, byteCount, 444 pChars + charIndex, charCount, null); 445 } 446 447 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 448 // So if you fix this, fix the others. Currently those include: 449 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 450 451 [System.Security.SecurityCritical] // auto-generated 452 [CLSCompliant(false)] 453 [System.Runtime.InteropServices.ComVisible(false)] GetChars(byte* bytes, int byteCount, char* chars, int charCount)454 public unsafe override int GetChars(byte* bytes, int byteCount, char* chars, int charCount) 455 { 456 // Validate Parameters 457 if (bytes == null || chars == null) 458 throw new ArgumentNullException(bytes == null ? "bytes" : "chars", 459 Environment.GetResourceString("ArgumentNull_Array")); 460 461 if (charCount < 0 || byteCount < 0) 462 throw new ArgumentOutOfRangeException((charCount<0 ? "charCount" : "byteCount"), 463 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 464 Contract.EndContractBlock(); 465 466 return GetChars(bytes, byteCount, chars, charCount, null); 467 } 468 469 // Returns a string containing the decoded representation of a range of 470 // bytes in a byte array. 471 // 472 // All of our public Encodings that don't use EncodingNLS must have this (including EncodingNLS) 473 // So if you fix this, fix the others. Currently those include: 474 // EncodingNLS, UTF7Encoding, UTF8Encoding, UTF32Encoding, ASCIIEncoding, UnicodeEncoding 475 // parent method is safe 476 477 [System.Security.SecuritySafeCritical] // auto-generated 478 [System.Runtime.InteropServices.ComVisible(false)] GetString(byte[] bytes, int index, int count)479 public override unsafe String GetString(byte[] bytes, int index, int count) 480 { 481 // Validate Parameters 482 if (bytes == null) 483 throw new ArgumentNullException("bytes", 484 Environment.GetResourceString("ArgumentNull_Array")); 485 486 if (index < 0 || count < 0) 487 throw new ArgumentOutOfRangeException((index < 0 ? "index" : "count"), 488 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 489 490 if (bytes.Length - index < count) 491 throw new ArgumentOutOfRangeException("bytes", 492 Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer")); 493 Contract.EndContractBlock(); 494 495 // Avoid problems with empty input buffer 496 if (bytes.Length == 0) return String.Empty; 497 498 fixed (byte* pBytes = bytes) 499 return String.CreateStringFromEncoding( 500 pBytes + index, count, this); 501 } 502 503 // 504 // End of standard methods copied from EncodingNLS.cs 505 // 506 507 [System.Security.SecurityCritical] // auto-generated GetByteCount(char* chars, int count, EncoderNLS baseEncoder)508 internal override unsafe int GetByteCount(char* chars, int count, EncoderNLS baseEncoder) 509 { 510 Contract.Assert(chars!=null, "[UTF7Encoding.GetByteCount]chars!=null"); 511 Contract.Assert(count >=0, "[UTF7Encoding.GetByteCount]count >=0"); 512 513 // Just call GetBytes with bytes == null 514 return GetBytes(chars, count, null, 0, baseEncoder); 515 } 516 517 [System.Security.SecurityCritical] // auto-generated GetBytes(char* chars, int charCount, byte* bytes, int byteCount, EncoderNLS baseEncoder)518 internal override unsafe int GetBytes(char* chars, int charCount, 519 byte* bytes, int byteCount, EncoderNLS baseEncoder) 520 { 521 Contract.Assert(byteCount >=0, "[UTF7Encoding.GetBytes]byteCount >=0"); 522 Contract.Assert(chars!=null, "[UTF7Encoding.GetBytes]chars!=null"); 523 Contract.Assert(charCount >=0, "[UTF7Encoding.GetBytes]charCount >=0"); 524 525 // Get encoder info 526 UTF7Encoding.Encoder encoder = (UTF7Encoding.Encoder)baseEncoder; 527 528 // Default bits & count 529 int bits = 0; 530 int bitCount = -1; 531 532 // prepare our helpers 533 Encoding.EncodingByteBuffer buffer = new Encoding.EncodingByteBuffer( 534 this, encoder, bytes, byteCount, chars, charCount); 535 536 if (encoder != null) 537 { 538 bits = encoder.bits; 539 bitCount = encoder.bitCount; 540 541 // May have had too many left over 542 while (bitCount >= 6) 543 { 544 bitCount -= 6; 545 // If we fail we'll never really have enough room 546 if (!buffer.AddByte(base64Bytes[(bits >> bitCount) & 0x3F])) 547 ThrowBytesOverflow(encoder, buffer.Count == 0); 548 } 549 } 550 551 while (buffer.MoreData) 552 { 553 char currentChar = buffer.GetNextChar(); 554 555 if (currentChar < 0x80 && directEncode[currentChar]) 556 { 557 if (bitCount >= 0) 558 { 559 if (bitCount > 0) 560 { 561 // Try to add the next byte 562 if (!buffer.AddByte(base64Bytes[bits << 6 - bitCount & 0x3F])) 563 break; // Stop here, didn't throw 564 565 bitCount = 0; 566 } 567 568 // Need to get emit '-' and our char, 2 bytes total 569 if (!buffer.AddByte((byte)'-')) 570 break; // Stop here, didn't throw 571 572 bitCount = -1; 573 } 574 575 // Need to emit our char 576 if (!buffer.AddByte((byte)currentChar)) 577 break; // Stop here, didn't throw 578 } 579 else if (bitCount < 0 && currentChar == '+') 580 { 581 if (!buffer.AddByte((byte)'+', (byte)'-')) 582 break; // Stop here, didn't throw 583 } 584 else 585 { 586 if (bitCount < 0) 587 { 588 // Need to emit a + and 12 bits (3 bytes) 589 // Only 12 of the 16 bits will be emitted this time, the other 4 wait 'til next time 590 if (!buffer.AddByte((byte)'+')) 591 break; // Stop here, didn't throw 592 593 // We're now in bit mode, but haven't stored data yet 594 bitCount = 0; 595 } 596 597 // Add our bits 598 bits = bits << 16 | currentChar; 599 bitCount += 16; 600 601 while (bitCount >= 6) 602 { 603 bitCount -= 6; 604 if (!buffer.AddByte(base64Bytes[(bits >> bitCount) & 0x3F])) 605 { 606 bitCount += 6; // We didn't use these bits 607 currentChar = buffer.GetNextChar(); // We're processing this char still, but AddByte 608 // --'d it when we ran out of space 609 break; // Stop here, not enough room for bytes 610 } 611 } 612 613 if (bitCount >= 6) 614 break; // Didn't have room to encode enough bits 615 } 616 } 617 618 // Now if we have bits left over we have to encode them. 619 // MustFlush may have been cleared by encoding.ThrowBytesOverflow earlier if converting 620 if (bitCount >= 0 && (encoder == null || encoder.MustFlush)) 621 { 622 // Do we have bits we have to stick in? 623 if (bitCount > 0) 624 { 625 if (buffer.AddByte(base64Bytes[(bits << (6 - bitCount)) & 0x3F])) 626 { 627 // Emitted spare bits, 0 bits left 628 bitCount = 0; 629 } 630 } 631 632 // If converting and failed bitCount above, then we'll fail this too 633 if (buffer.AddByte((byte)'-')) 634 { 635 // turned off bit mode'; 636 bits = 0; 637 bitCount = -1; 638 } 639 else 640 // If not successful, convert will maintain state for next time, also 641 // AddByte will have decremented our char count, however we need it to remain the same 642 buffer.GetNextChar(); 643 } 644 645 // Do we have an encoder we're allowed to use? 646 // bytes == null if counting, so don't use encoder then 647 if (bytes != null && encoder != null) 648 { 649 // We already cleared bits & bitcount for mustflush case 650 encoder.bits = bits; 651 encoder.bitCount = bitCount; 652 encoder.m_charsUsed = buffer.CharsUsed; 653 } 654 655 return buffer.Count; 656 } 657 658 [System.Security.SecurityCritical] // auto-generated GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder)659 internal override unsafe int GetCharCount(byte* bytes, int count, DecoderNLS baseDecoder) 660 { 661 Contract.Assert(count >=0, "[UTF7Encoding.GetCharCount]count >=0"); 662 Contract.Assert(bytes!=null, "[UTF7Encoding.GetCharCount]bytes!=null"); 663 664 // Just call GetChars with null char* to do counting 665 return GetChars(bytes, count, null, 0, baseDecoder); 666 } 667 668 [System.Security.SecurityCritical] // auto-generated GetChars(byte* bytes, int byteCount, char* chars, int charCount, DecoderNLS baseDecoder)669 internal override unsafe int GetChars(byte* bytes, int byteCount, 670 char* chars, int charCount, DecoderNLS baseDecoder) 671 { 672 Contract.Assert(byteCount >=0, "[UTF7Encoding.GetChars]byteCount >=0"); 673 Contract.Assert(bytes!=null, "[UTF7Encoding.GetChars]bytes!=null"); 674 Contract.Assert(charCount >=0, "[UTF7Encoding.GetChars]charCount >=0"); 675 676 // Might use a decoder 677 UTF7Encoding.Decoder decoder = (UTF7Encoding.Decoder) baseDecoder; 678 679 // Get our output buffer info. 680 Encoding.EncodingCharBuffer buffer = new Encoding.EncodingCharBuffer( 681 this, decoder, chars, charCount, bytes, byteCount); 682 683 // Get decoder info 684 int bits = 0; 685 int bitCount = -1; 686 bool firstByte = false; 687 if (decoder != null) 688 { 689 bits = decoder.bits; 690 bitCount = decoder.bitCount; 691 firstByte = decoder.firstByte; 692 693 Contract.Assert(firstByte == false || decoder.bitCount <= 0, 694 "[UTF7Encoding.GetChars]If remembered bits, then first byte flag shouldn't be set"); 695 } 696 697 // We may have had bits in the decoder that we couldn't output last time, so do so now 698 if (bitCount >= 16) 699 { 700 // Check our decoder buffer 701 if (!buffer.AddChar((char)((bits >> (bitCount - 16)) & 0xFFFF))) 702 ThrowCharsOverflow(decoder, true); // Always throw, they need at least 1 char even in Convert 703 704 // Used this one, clean up extra bits 705 bitCount -= 16; 706 } 707 708 // Loop through the input 709 while (buffer.MoreData) 710 { 711 byte currentByte = buffer.GetNextByte(); 712 int c; 713 714 if (bitCount >= 0) 715 { 716 // 717 // Modified base 64 encoding. 718 // 719 sbyte v; 720 if (currentByte < 0x80 && ((v = base64Values[currentByte]) >=0)) 721 { 722 firstByte = false; 723 bits = (bits << 6) | ((byte)v); 724 bitCount += 6; 725 if (bitCount >= 16) 726 { 727 c = (bits >> (bitCount - 16)) & 0xFFFF; 728 bitCount -= 16; 729 } 730 // If not enough bits just continue 731 else continue; 732 } 733 else 734 { 735 // If it wasn't a base 64 byte, everything's going to turn off base 64 mode 736 bitCount = -1; 737 738 if (currentByte != '-') 739 { 740 // >= 0x80 (because of 1st if statemtn) 741 // We need this check since the base64Values[b] check below need b <= 0x7f. 742 // This is not a valid base 64 byte. Terminate the shifted-sequence and 743 // emit this byte. 744 745 // not in base 64 table 746 // According to the RFC 1642 and the example code of UTF-7 747 // in Unicode 2.0, we should just zero-extend the invalid UTF7 byte 748 749 // Chars won't be updated unless this works, try to fallback 750 if (!buffer.Fallback(currentByte)) 751 break; // Stop here, didn't throw 752 753 // Used that byte, we're done with it 754 continue; 755 } 756 757 // 758 // The encoding for '+' is "+-". 759 // 760 if (firstByte) c = '+'; 761 // We just turn it off if not emitting a +, so we're done. 762 else continue; 763 } 764 // 765 // End of modified base 64 encoding block. 766 // 767 } 768 else if (currentByte == '+') 769 { 770 // 771 // Found the start of a modified base 64 encoding block or a plus sign. 772 // 773 bitCount = 0; 774 firstByte = true; 775 continue; 776 } 777 else 778 { 779 // Normal character 780 if (currentByte >= 0x80) 781 { 782 // Try to fallback 783 if (!buffer.Fallback(currentByte)) 784 break; // Stop here, didn't throw 785 786 // Done falling back 787 continue; 788 } 789 790 // Use the normal character 791 c = currentByte; 792 } 793 794 if (c >= 0) 795 { 796 // Check our buffer 797 if (!buffer.AddChar((char)c)) 798 { 799 // No room. If it was a plain char we'll try again later. 800 // Note, we'll consume this byte and stick it in decoder, even if we can't output it 801 if (bitCount >= 0) // Can we rememmber this byte (char) 802 { 803 buffer.AdjustBytes(+1); // Need to readd the byte that AddChar subtracted when it failed 804 bitCount += 16; // We'll still need that char we have in our bits 805 } 806 break; // didn't throw, stop 807 } 808 } 809 } 810 811 // Stick stuff in the decoder if we can (chars == null if counting, so don't store decoder) 812 if (chars != null && decoder != null) 813 { 814 // MustFlush? (Could've been cleared by ThrowCharsOverflow if Convert & didn't reach end of buffer) 815 if (decoder.MustFlush) 816 { 817 // RFC doesn't specify what would happen if we have non-0 leftover bits, we just drop them 818 decoder.bits = 0; 819 decoder.bitCount = -1; 820 decoder.firstByte = false; 821 } 822 else 823 { 824 decoder.bits = bits; 825 decoder.bitCount = bitCount; 826 decoder.firstByte = firstByte; 827 } 828 decoder.m_bytesUsed = buffer.BytesUsed; 829 } 830 // else ignore any hanging bits. 831 832 // Return our count 833 return buffer.Count; 834 } 835 836 GetDecoder()837 public override System.Text.Decoder GetDecoder() 838 { 839 return new UTF7Encoding.Decoder(this); 840 } 841 842 GetEncoder()843 public override System.Text.Encoder GetEncoder() 844 { 845 return new UTF7Encoding.Encoder(this); 846 } 847 848 GetMaxByteCount(int charCount)849 public override int GetMaxByteCount(int charCount) 850 { 851 if (charCount < 0) 852 throw new ArgumentOutOfRangeException("charCount", 853 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 854 Contract.EndContractBlock(); 855 856 // Suppose that every char can not be direct-encoded, we know that 857 // a byte can encode 6 bits of the Unicode character. And we will 858 // also need two extra bytes for the shift-in ('+') and shift-out ('-') mark. 859 // Therefore, the max byte should be: 860 // byteCount = 2 + Math.Ceiling((double)charCount * 16 / 6); 861 // That is always <= 2 + 3 * charCount; 862 // Longest case is alternating encoded, direct, encoded data for 5 + 1 + 5... bytes per char. 863 // UTF7 doesn't have left over surrogates, but if no input we may need an output - to turn off 864 // encoding if MustFlush is true. 865 866 // Its easiest to think of this as 2 bytes to turn on/off the base64 mode, then 3 bytes per char. 867 // 3 bytes is 18 bits of encoding, which is more than we need, but if its direct encoded then 3 868 // bytes allows us to turn off and then back on base64 mode if necessary. 869 870 // Note that UTF7 encoded surrogates individually and isn't worried about mismatches, so all 871 // code points are encodable int UTF7. 872 long byteCount = (long)charCount * 3 + 2; 873 874 // check for overflow 875 if (byteCount > 0x7fffffff) 876 throw new ArgumentOutOfRangeException("charCount", Environment.GetResourceString("ArgumentOutOfRange_GetByteCountOverflow")); 877 878 return (int)byteCount; 879 } 880 881 GetMaxCharCount(int byteCount)882 public override int GetMaxCharCount(int byteCount) 883 { 884 if (byteCount < 0) 885 throw new ArgumentOutOfRangeException("byteCount", 886 Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); 887 Contract.EndContractBlock(); 888 889 // Worst case is 1 char per byte. Minimum 1 for left over bits in case decoder is being flushed 890 // Also note that we ignore extra bits (per spec), so UTF7 doesn't have unknown in this direction. 891 int charCount = byteCount; 892 if (charCount == 0) charCount = 1; 893 894 return charCount; 895 } 896 897 [Serializable] 898 // Of all the amazing things... This MUST be Decoder so that our com name 899 // for System.Text.Decoder doesn't change 900 private class Decoder : DecoderNLS, ISerializable 901 { 902 /*private*/ internal int bits; 903 /*private*/ internal int bitCount; 904 /*private*/ internal bool firstByte; 905 Decoder(UTF7Encoding encoding)906 public Decoder(UTF7Encoding encoding) : base (encoding) 907 { 908 // base calls reset 909 } 910 911 // Constructor called by serialization, have to handle deserializing from Everett Decoder(SerializationInfo info, StreamingContext context)912 internal Decoder(SerializationInfo info, StreamingContext context) 913 { 914 // Any info? 915 if (info==null) throw new ArgumentNullException("info"); 916 Contract.EndContractBlock(); 917 918 // Get common info 919 this.bits = (int)info.GetValue("bits", typeof(int)); 920 this.bitCount = (int)info.GetValue("bitCount", typeof(int)); 921 this.firstByte = (bool)info.GetValue("firstByte", typeof(bool)); 922 this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding)); 923 } 924 925 #if FEATURE_SERIALIZATION 926 // ISerializable implementation, get data for this object 927 [System.Security.SecurityCritical] // auto-generated_required ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)928 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 929 { 930 // Any info? 931 if (info==null) throw new ArgumentNullException("info"); 932 Contract.EndContractBlock(); 933 934 // Save Whidbey data 935 info.AddValue("encoding", this.m_encoding); 936 info.AddValue("bits", this.bits); 937 info.AddValue("bitCount", this.bitCount); 938 info.AddValue("firstByte", this.firstByte); 939 } 940 #endif 941 Reset()942 public override void Reset() 943 { 944 this.bits = 0; 945 this.bitCount = -1; 946 this.firstByte = false; 947 if (m_fallbackBuffer != null) 948 m_fallbackBuffer.Reset(); 949 } 950 951 // Anything left in our encoder? 952 internal override bool HasState 953 { 954 get 955 { 956 // NOTE: This forces the last -, which some encoder might not encode. If we 957 // don't see it we don't think we're done reading. 958 return (this.bitCount != -1); 959 } 960 } 961 } 962 963 [Serializable] 964 // Of all the amazing things... This MUST be Encoder so that our com name 965 // for System.Text.Encoder doesn't change 966 private class Encoder : EncoderNLS, ISerializable 967 { 968 /*private*/ internal int bits; 969 /*private*/ internal int bitCount; 970 Encoder(UTF7Encoding encoding)971 public Encoder(UTF7Encoding encoding) : base(encoding) 972 { 973 // base calls reset 974 } 975 976 // Constructor called by serialization, have to handle deserializing from Everett Encoder(SerializationInfo info, StreamingContext context)977 internal Encoder(SerializationInfo info, StreamingContext context) 978 { 979 // Any info? 980 if (info==null) throw new ArgumentNullException("info"); 981 Contract.EndContractBlock(); 982 983 // Get common info 984 this.bits = (int)info.GetValue("bits", typeof(int)); 985 this.bitCount = (int)info.GetValue("bitCount", typeof(int)); 986 this.m_encoding = (Encoding)info.GetValue("encoding", typeof(Encoding)); 987 } 988 989 #if FEATURE_SERIALIZATION 990 // ISerializable implementation, get data for this object 991 [System.Security.SecurityCritical] // auto-generated_required ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)992 void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 993 { 994 // Any info? 995 if (info==null) throw new ArgumentNullException("info"); 996 Contract.EndContractBlock(); 997 998 // Save Whidbey data 999 info.AddValue("encoding", this.m_encoding); 1000 info.AddValue("bits", this.bits); 1001 info.AddValue("bitCount", this.bitCount); 1002 } 1003 #endif 1004 Reset()1005 public override void Reset() 1006 { 1007 this.bitCount = -1; 1008 this.bits = 0; 1009 if (m_fallbackBuffer != null) 1010 m_fallbackBuffer.Reset(); 1011 } 1012 1013 // Anything left in our encoder? 1014 internal override bool HasState 1015 { 1016 get 1017 { 1018 return (this.bits != 0 || this.bitCount != -1); 1019 } 1020 } 1021 } 1022 1023 // Preexisting UTF7 behavior for bad bytes was just to spit out the byte as the next char 1024 // and turn off base64 mode if it was in that mode. We still exit the mode, but now we fallback. 1025 [Serializable] 1026 internal sealed class DecoderUTF7Fallback : DecoderFallback 1027 { 1028 // Construction. Default replacement fallback uses no best fit and ? replacement string DecoderUTF7Fallback()1029 public DecoderUTF7Fallback() 1030 { 1031 } 1032 CreateFallbackBuffer()1033 public override DecoderFallbackBuffer CreateFallbackBuffer() 1034 { 1035 return new DecoderUTF7FallbackBuffer(this); 1036 } 1037 1038 // Maximum number of characters that this instance of this fallback could return 1039 public override int MaxCharCount 1040 { 1041 get 1042 { 1043 // returns 1 char per bad byte 1044 return 1; 1045 } 1046 } 1047 Equals(Object value)1048 public override bool Equals(Object value) 1049 { 1050 DecoderUTF7Fallback that = value as DecoderUTF7Fallback; 1051 if (that != null) 1052 { 1053 return true; 1054 } 1055 return (false); 1056 } 1057 GetHashCode()1058 public override int GetHashCode() 1059 { 1060 return 984; 1061 } 1062 } 1063 1064 internal sealed class DecoderUTF7FallbackBuffer : DecoderFallbackBuffer 1065 { 1066 // Store our default string 1067 char cFallback = (char)0; 1068 int iCount = -1; 1069 int iSize; 1070 1071 // Construction DecoderUTF7FallbackBuffer(DecoderUTF7Fallback fallback)1072 public DecoderUTF7FallbackBuffer(DecoderUTF7Fallback fallback) 1073 { 1074 } 1075 1076 // Fallback Methods Fallback(byte[] bytesUnknown, int index)1077 public override bool Fallback(byte[] bytesUnknown, int index) 1078 { 1079 // We expect no previous fallback in our buffer 1080 Contract.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.Fallback] Can't have recursive fallbacks"); 1081 Contract.Assert(bytesUnknown.Length == 1, "[DecoderUTF7FallbackBuffer.Fallback] Only possible fallback case should be 1 unknown byte"); 1082 1083 // Go ahead and get our fallback 1084 cFallback = (char)bytesUnknown[0]; 1085 1086 // Any of the fallback characters can be handled except for 0 1087 if (cFallback == 0) 1088 { 1089 return false; 1090 } 1091 1092 iCount = iSize = 1; 1093 1094 return true; 1095 } 1096 GetNextChar()1097 public override char GetNextChar() 1098 { 1099 if (iCount-- > 0) 1100 return cFallback; 1101 1102 // Note: this means that 0 in UTF7 stream will never be emitted. 1103 return (char)0; 1104 } 1105 MovePrevious()1106 public override bool MovePrevious() 1107 { 1108 if (iCount >= 0) 1109 { 1110 iCount++; 1111 } 1112 1113 // return true if we were allowed to do this 1114 return (iCount >= 0 && iCount <= iSize); 1115 } 1116 1117 // Return # of chars left in this fallback 1118 public override int Remaining 1119 { 1120 get 1121 { 1122 return (iCount > 0) ? iCount : 0; 1123 } 1124 } 1125 1126 // Clear the buffer 1127 [System.Security.SecuritySafeCritical] // overrides public transparent member Reset()1128 public override unsafe void Reset() 1129 { 1130 iCount = -1; 1131 byteStart = null; 1132 } 1133 1134 // This version just counts the fallback and doesn't actually copy anything. 1135 [System.Security.SecurityCritical] // auto-generated InternalFallback(byte[] bytes, byte* pBytes)1136 internal unsafe override int InternalFallback(byte[] bytes, byte* pBytes) 1137 // Right now this has both bytes and bytes[], since we might have extra bytes, hence the 1138 // array, and we might need the index, hence the byte* 1139 { 1140 // We expect no previous fallback in our buffer 1141 Contract.Assert(iCount < 0, "[DecoderUTF7FallbackBuffer.InternalFallback] Can't have recursive fallbacks"); 1142 if (bytes.Length != 1) 1143 { 1144 throw new ArgumentException(Environment.GetResourceString("Argument_InvalidCharSequenceNoIndex")); 1145 } 1146 1147 // Can't fallback a byte 0, so return for that case, 1 otherwise. 1148 return bytes[0] == 0 ? 0 : 1; 1149 } 1150 } 1151 1152 } 1153 } 1154