diff options
Diffstat (limited to 'MdePkg/Library/BaseLib/String.c')
-rw-r--r-- | MdePkg/Library/BaseLib/String.c | 564 |
1 files changed, 511 insertions, 53 deletions
diff --git a/MdePkg/Library/BaseLib/String.c b/MdePkg/Library/BaseLib/String.c index 9dcfb7268832..a9ad94b9380b 100644 --- a/MdePkg/Library/BaseLib/String.c +++ b/MdePkg/Library/BaseLib/String.c @@ -1,14 +1,8 @@ /** @file Unicode and ASCII string primitives. - Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR> - This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php. - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -73,7 +67,7 @@ StrCpy ( /** [ATTENTION] This function will be deprecated for security reason. - Copies up to a specified length from one Null-terminated Unicode string to + Copies up to a specified length from one Null-terminated Unicode string to another Null-terminated Unicode string and returns the new Unicode string. This function copies the contents of the Unicode string Source to the Unicode @@ -89,7 +83,7 @@ StrCpy ( If Length > 0 and Source is NULL, then ASSERT(). If Length > 0 and Source is not aligned on a 16-bit boundary, then ASSERT(). If Source and Destination overlap, then ASSERT(). - If PcdMaximumUnicodeStringLength is not zero, and Length is greater than + If PcdMaximumUnicodeStringLength is not zero, and Length is greater than PcdMaximumUnicodeStringLength, then ASSERT(). If PcdMaximumUnicodeStringLength is not zero, and Source contains more than PcdMaximumUnicodeStringLength Unicode characters, not including the Null-terminator, @@ -188,7 +182,7 @@ StrLen ( Returns the size of a Null-terminated Unicode string in bytes, including the Null terminator. - This function returns the size, in bytes, of the Null-terminated Unicode string + This function returns the size, in bytes, of the Null-terminated Unicode string specified by String. If String is NULL, then ASSERT(). @@ -262,7 +256,7 @@ StrCmp ( /** Compares up to a specified length the contents of two Null-terminated Unicode strings, and returns the difference between the first mismatched Unicode characters. - + This function compares the Null-terminated Unicode string FirstString to the Null-terminated Unicode string SecondString. At most, Length Unicode characters will be compared. If Length is 0, then 0 is returned. If @@ -382,8 +376,8 @@ StrCat ( /** [ATTENTION] This function will be deprecated for security reason. - Concatenates up to a specified length one Null-terminated Unicode to the end - of another Null-terminated Unicode string, and returns the concatenated + Concatenates up to a specified length one Null-terminated Unicode to the end + of another Null-terminated Unicode string, and returns the concatenated Unicode string. This function concatenates two Null-terminated Unicode strings. The contents @@ -399,7 +393,7 @@ StrCat ( If Length > 0 and Source is NULL, then ASSERT(). If Length > 0 and Source is not aligned on a 16-bit boundary, then ASSERT(). If Source and Destination overlap, then ASSERT(). - If PcdMaximumUnicodeStringLength is not zero, and Length is greater than + If PcdMaximumUnicodeStringLength is not zero, and Length is greater than PcdMaximumUnicodeStringLength, then ASSERT(). If PcdMaximumUnicodeStringLength is not zero, and Destination contains more than PcdMaximumUnicodeStringLength Unicode characters, not including the @@ -492,13 +486,13 @@ StrStr ( while (*String != L'\0') { SearchStringTmp = SearchString; FirstMatch = String; - - while ((*String == *SearchStringTmp) + + while ((*String == *SearchStringTmp) && (*String != L'\0')) { String++; SearchStringTmp++; - } - + } + if (*SearchStringTmp == L'\0') { return (CHAR16 *) FirstMatch; } @@ -516,7 +510,7 @@ StrStr ( /** Check if a Unicode character is a decimal character. - This internal function checks if a Unicode character is a + This internal function checks if a Unicode character is a decimal character. The valid decimal character is from L'0' to L'9'. @@ -536,7 +530,7 @@ InternalIsDecimalDigitCharacter ( } /** - Convert a Unicode character to upper case only if + Convert a Unicode character to upper case only if it maps to a valid small-case ASCII character. This internal function only deal with Unicode character @@ -552,7 +546,7 @@ InternalIsDecimalDigitCharacter ( **/ CHAR16 EFIAPI -InternalCharToUpper ( +CharToUpper ( IN CHAR16 Char ) { @@ -568,7 +562,7 @@ InternalCharToUpper ( This internal function only deal with Unicode character which maps to a valid hexadecimal ASII character, i.e. - L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other + L'0' to L'9', L'a' to L'f' or L'A' to L'F'. For other Unicode character, the value returned does not make sense. @param Char The character to convert. @@ -586,14 +580,14 @@ InternalHexCharToUintn ( return Char - L'0'; } - return (10 + InternalCharToUpper (Char) - L'A'); + return (10 + CharToUpper (Char) - L'A'); } /** Check if a Unicode character is a hexadecimal character. - This internal function checks if a Unicode character is a - decimal character. The valid hexadecimal character is + This internal function checks if a Unicode character is a + decimal character. The valid hexadecimal character is L'0' to L'9', L'a' to L'f', or L'A' to L'F'. @@ -703,7 +697,7 @@ StrDecimalToUint64 ( ) { UINT64 Result; - + StrDecimalToUint64S (String, (CHAR16 **) NULL, &Result); return Result; } @@ -806,7 +800,7 @@ StrHexToUint64 ( /** Check if a ASCII character is a decimal character. - This internal function checks if a Unicode character is a + This internal function checks if a Unicode character is a decimal character. The valid decimal character is from '0' to '9'. @@ -828,8 +822,8 @@ InternalAsciiIsDecimalDigitCharacter ( /** Check if a ASCII character is a hexadecimal character. - This internal function checks if a ASCII character is a - decimal character. The valid hexadecimal character is + This internal function checks if a ASCII character is a + decimal character. The valid hexadecimal character is L'0' to L'9', L'a' to L'f', or L'A' to L'F'. @@ -915,7 +909,7 @@ UnicodeStrToAsciiStr ( ReturnValue = Destination; while (*Source != '\0') { // - // If any Unicode characters in Source contain + // If any Unicode characters in Source contain // non-zero value in the upper 8 bits, then ASSERT(). // ASSERT (*Source < 0x100); @@ -987,7 +981,7 @@ AsciiStrCpy ( /** [ATTENTION] This function will be deprecated for security reason. - Copies up to a specified length one Null-terminated ASCII string to another + Copies up to a specified length one Null-terminated ASCII string to another Null-terminated ASCII string and returns the new ASCII string. This function copies the contents of the ASCII string Source to the ASCII @@ -1000,7 +994,7 @@ AsciiStrCpy ( If Destination is NULL, then ASSERT(). If Source is NULL, then ASSERT(). If Source and Destination overlap, then ASSERT(). - If PcdMaximumAsciiStringLength is not zero, and Length is greater than + If PcdMaximumAsciiStringLength is not zero, and Length is greater than PcdMaximumAsciiStringLength, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and Source contains more than PcdMaximumAsciiStringLength ASCII characters, not including the Null-terminator, @@ -1176,12 +1170,12 @@ AsciiStrCmp ( @param Chr one Ascii character - @return The uppercase value of Ascii character + @return The uppercase value of Ascii character **/ CHAR8 EFIAPI -InternalBaseLibAsciiToUpper ( +AsciiCharToUpper ( IN CHAR8 Chr ) { @@ -1193,7 +1187,7 @@ InternalBaseLibAsciiToUpper ( This internal function only deal with Unicode character which maps to a valid hexadecimal ASII character, i.e. - '0' to '9', 'a' to 'f' or 'A' to 'F'. For other + '0' to '9', 'a' to 'f' or 'A' to 'F'. For other ASCII character, the value returned does not make sense. @param Char The character to convert. @@ -1211,7 +1205,7 @@ InternalAsciiHexCharToUintn ( return Char - '0'; } - return (10 + InternalBaseLibAsciiToUpper (Char) - 'A'); + return (10 + AsciiCharToUpper (Char) - 'A'); } @@ -1260,13 +1254,13 @@ AsciiStriCmp ( ASSERT (AsciiStrSize (FirstString)); ASSERT (AsciiStrSize (SecondString)); - UpperFirstString = InternalBaseLibAsciiToUpper (*FirstString); - UpperSecondString = InternalBaseLibAsciiToUpper (*SecondString); - while ((*FirstString != '\0') && (UpperFirstString == UpperSecondString)) { + UpperFirstString = AsciiCharToUpper (*FirstString); + UpperSecondString = AsciiCharToUpper (*SecondString); + while ((*FirstString != '\0') && (*SecondString != '\0') && (UpperFirstString == UpperSecondString)) { FirstString++; SecondString++; - UpperFirstString = InternalBaseLibAsciiToUpper (*FirstString); - UpperSecondString = InternalBaseLibAsciiToUpper (*SecondString); + UpperFirstString = AsciiCharToUpper (*FirstString); + UpperSecondString = AsciiCharToUpper (*SecondString); } return UpperFirstString - UpperSecondString; @@ -1285,7 +1279,7 @@ AsciiStriCmp ( If Length > 0 and FirstString is NULL, then ASSERT(). If Length > 0 and SecondString is NULL, then ASSERT(). - If PcdMaximumAsciiStringLength is not zero, and Length is greater than + If PcdMaximumAsciiStringLength is not zero, and Length is greater than PcdMaximumAsciiStringLength, then ASSERT(). If PcdMaximumAsciiStringLength is not zero, and FirstString contains more than PcdMaximumAsciiStringLength ASCII characters, not including the Null-terminator, @@ -1297,7 +1291,7 @@ AsciiStriCmp ( @param FirstString A pointer to a Null-terminated ASCII string. @param SecondString A pointer to a Null-terminated ASCII string. @param Length The maximum number of ASCII characters for compare. - + @retval ==0 FirstString is identical to SecondString. @retval !=0 FirstString is not identical to SecondString. @@ -1386,8 +1380,8 @@ AsciiStrCat ( /** [ATTENTION] This function will be deprecated for security reason. - Concatenates up to a specified length one Null-terminated ASCII string to - the end of another Null-terminated ASCII string, and returns the + Concatenates up to a specified length one Null-terminated ASCII string to + the end of another Null-terminated ASCII string, and returns the concatenated ASCII string. This function concatenates two Null-terminated ASCII strings. The contents @@ -1491,13 +1485,13 @@ AsciiStrStr ( while (*String != '\0') { SearchStringTmp = SearchString; FirstMatch = String; - - while ((*String == *SearchStringTmp) + + while ((*String == *SearchStringTmp) && (*String != '\0')) { String++; SearchStringTmp++; - } - + } + if (*SearchStringTmp == '\0') { return (CHAR8 *) FirstMatch; } @@ -1549,7 +1543,7 @@ AsciiStrDecimalToUintn ( ) { UINTN Result; - + AsciiStrDecimalToUintnS (String, (CHAR8 **) NULL, &Result); return Result; } @@ -1592,7 +1586,7 @@ AsciiStrDecimalToUint64 ( ) { UINT64 Result; - + AsciiStrDecimalToUint64S (String, (CHAR8 **) NULL, &Result); return Result; } @@ -1746,7 +1740,7 @@ AsciiStrToUnicodeStr ( ReturnValue = Destination; while (*Source != '\0') { - *(Destination++) = (CHAR16) *(Source++); + *(Destination++) = (CHAR16)(UINT8) *(Source++); } // // End the Destination with a NULL. @@ -1763,6 +1757,470 @@ AsciiStrToUnicodeStr ( #endif +STATIC CHAR8 EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +/** + Convert binary data to a Base64 encoded ascii string based on RFC4648. + + Produce a Null-terminated Ascii string in the output buffer specified by Destination and DestinationSize. + The Ascii string is produced by converting the data string specified by Source and SourceLength. + + @param Source Input UINT8 data + @param SourceLength Number of UINT8 bytes of data + @param Destination Pointer to output string buffer + @param DestinationSize Size of ascii buffer. Set to 0 to get the size needed. + Caller is responsible for passing in buffer of DestinationSize + + @retval RETURN_SUCCESS When ascii buffer is filled in. + @retval RETURN_INVALID_PARAMETER If Source is NULL or DestinationSize is NULL. + @retval RETURN_INVALID_PARAMETER If SourceLength or DestinationSize is bigger than (MAX_ADDRESS - (UINTN)Destination). + @retval RETURN_BUFFER_TOO_SMALL If SourceLength is 0 and DestinationSize is <1. + @retval RETURN_BUFFER_TOO_SMALL If Destination is NULL or DestinationSize is smaller than required buffersize. + +**/ +RETURN_STATUS +EFIAPI +Base64Encode ( + IN CONST UINT8 *Source, + IN UINTN SourceLength, + OUT CHAR8 *Destination OPTIONAL, + IN OUT UINTN *DestinationSize + ) +{ + + UINTN RequiredSize; + UINTN Left; + + // + // Check pointers, and SourceLength is valid + // + if ((Source == NULL) || (DestinationSize == NULL)) { + return RETURN_INVALID_PARAMETER; + } + + // + // Allow for RFC 4648 test vector 1 + // + if (SourceLength == 0) { + if (*DestinationSize < 1) { + *DestinationSize = 1; + return RETURN_BUFFER_TOO_SMALL; + } + *DestinationSize = 1; + *Destination = '\0'; + return RETURN_SUCCESS; + } + + // + // Check if SourceLength or DestinationSize is valid + // + if ((SourceLength >= (MAX_ADDRESS - (UINTN)Source)) || (*DestinationSize >= (MAX_ADDRESS - (UINTN)Destination))){ + return RETURN_INVALID_PARAMETER; + } + + // + // 4 ascii per 3 bytes + NULL + // + RequiredSize = ((SourceLength + 2) / 3) * 4 + 1; + if ((Destination == NULL) || *DestinationSize < RequiredSize) { + *DestinationSize = RequiredSize; + return RETURN_BUFFER_TOO_SMALL; + } + + Left = SourceLength; + + // + // Encode 24 bits (three bytes) into 4 ascii characters + // + while (Left >= 3) { + + *Destination++ = EncodingTable[( Source[0] & 0xfc) >> 2 ]; + *Destination++ = EncodingTable[((Source[0] & 0x03) << 4) + ((Source[1] & 0xf0) >> 4)]; + *Destination++ = EncodingTable[((Source[1] & 0x0f) << 2) + ((Source[2] & 0xc0) >> 6)]; + *Destination++ = EncodingTable[( Source[2] & 0x3f)]; + Left -= 3; + Source += 3; + } + + // + // Handle the remainder, and add padding '=' characters as necessary. + // + switch (Left) { + case 0: + + // + // No bytes Left, done. + // + break; + case 1: + + // + // One more data byte, two pad characters + // + *Destination++ = EncodingTable[( Source[0] & 0xfc) >> 2]; + *Destination++ = EncodingTable[((Source[0] & 0x03) << 4)]; + *Destination++ = '='; + *Destination++ = '='; + break; + case 2: + + // + // Two more data bytes, and one pad character + // + *Destination++ = EncodingTable[( Source[0] & 0xfc) >> 2]; + *Destination++ = EncodingTable[((Source[0] & 0x03) << 4) + ((Source[1] & 0xf0) >> 4)]; + *Destination++ = EncodingTable[((Source[1] & 0x0f) << 2)]; + *Destination++ = '='; + break; + } + // + // Add terminating NULL + // + *Destination = '\0'; + return RETURN_SUCCESS; +} + +/** + Decode Base64 ASCII encoded data to 8-bit binary representation, based on + RFC4648. + + Decoding occurs according to "Table 1: The Base 64 Alphabet" in RFC4648. + + Whitespace is ignored at all positions: + - 0x09 ('\t') horizontal tab + - 0x0A ('\n') new line + - 0x0B ('\v') vertical tab + - 0x0C ('\f') form feed + - 0x0D ('\r') carriage return + - 0x20 (' ') space + + The minimum amount of required padding (with ASCII 0x3D, '=') is tolerated + and enforced at the end of the Base64 ASCII encoded data, and only there. + + Other characters outside of the encoding alphabet cause the function to + reject the Base64 ASCII encoded data. + + @param[in] Source Array of CHAR8 elements containing the Base64 + ASCII encoding. May be NULL if SourceSize is + zero. + + @param[in] SourceSize Number of CHAR8 elements in Source. + + @param[out] Destination Array of UINT8 elements receiving the decoded + 8-bit binary representation. Allocated by the + caller. May be NULL if DestinationSize is + zero on input. If NULL, decoding is + performed, but the 8-bit binary + representation is not stored. If non-NULL and + the function returns an error, the contents + of Destination are indeterminate. + + @param[in,out] DestinationSize On input, the number of UINT8 elements that + the caller allocated for Destination. On + output, if the function returns + RETURN_SUCCESS or RETURN_BUFFER_TOO_SMALL, + the number of UINT8 elements that are + required for decoding the Base64 ASCII + representation. If the function returns a + value different from both RETURN_SUCCESS and + RETURN_BUFFER_TOO_SMALL, then DestinationSize + is indeterminate on output. + + @retval RETURN_SUCCESS SourceSize CHAR8 elements at Source have + been decoded to on-output DestinationSize + UINT8 elements at Destination. Note that + RETURN_SUCCESS covers the case when + DestinationSize is zero on input, and + Source decodes to zero bytes (due to + containing at most ignored whitespace). + + @retval RETURN_BUFFER_TOO_SMALL The input value of DestinationSize is not + large enough for decoding SourceSize CHAR8 + elements at Source. The required number of + UINT8 elements has been stored to + DestinationSize. + + @retval RETURN_INVALID_PARAMETER DestinationSize is NULL. + + @retval RETURN_INVALID_PARAMETER Source is NULL, but SourceSize is not zero. + + @retval RETURN_INVALID_PARAMETER Destination is NULL, but DestinationSize is + not zero on input. + + @retval RETURN_INVALID_PARAMETER Source is non-NULL, and (Source + + SourceSize) would wrap around MAX_ADDRESS. + + @retval RETURN_INVALID_PARAMETER Destination is non-NULL, and (Destination + + DestinationSize) would wrap around + MAX_ADDRESS, as specified on input. + + @retval RETURN_INVALID_PARAMETER None of Source and Destination are NULL, + and CHAR8[SourceSize] at Source overlaps + UINT8[DestinationSize] at Destination, as + specified on input. + + @retval RETURN_INVALID_PARAMETER Invalid CHAR8 element encountered in + Source. +**/ +RETURN_STATUS +EFIAPI +Base64Decode ( + IN CONST CHAR8 *Source OPTIONAL, + IN UINTN SourceSize, + OUT UINT8 *Destination OPTIONAL, + IN OUT UINTN *DestinationSize + ) +{ + BOOLEAN PaddingMode; + UINTN SixBitGroupsConsumed; + UINT32 Accumulator; + UINTN OriginalDestinationSize; + UINTN SourceIndex; + CHAR8 SourceChar; + UINT32 Base64Value; + UINT8 DestinationOctet; + + if (DestinationSize == NULL) { + return RETURN_INVALID_PARAMETER; + } + + // + // Check Source array validity. + // + if (Source == NULL) { + if (SourceSize > 0) { + // + // At least one CHAR8 element at NULL Source. + // + return RETURN_INVALID_PARAMETER; + } + } else if (SourceSize > MAX_ADDRESS - (UINTN)Source) { + // + // Non-NULL Source, but it wraps around. + // + return RETURN_INVALID_PARAMETER; + } + + // + // Check Destination array validity. + // + if (Destination == NULL) { + if (*DestinationSize > 0) { + // + // At least one UINT8 element at NULL Destination. + // + return RETURN_INVALID_PARAMETER; + } + } else if (*DestinationSize > MAX_ADDRESS - (UINTN)Destination) { + // + // Non-NULL Destination, but it wraps around. + // + return RETURN_INVALID_PARAMETER; + } + + // + // Check for overlap. + // + if (Source != NULL && Destination != NULL) { + // + // Both arrays have been provided, and we know from earlier that each array + // is valid in itself. + // + if ((UINTN)Source + SourceSize <= (UINTN)Destination) { + // + // Source array precedes Destination array, OK. + // + } else if ((UINTN)Destination + *DestinationSize <= (UINTN)Source) { + // + // Destination array precedes Source array, OK. + // + } else { + // + // Overlap. + // + return RETURN_INVALID_PARAMETER; + } + } + + // + // Decoding loop setup. + // + PaddingMode = FALSE; + SixBitGroupsConsumed = 0; + Accumulator = 0; + OriginalDestinationSize = *DestinationSize; + *DestinationSize = 0; + + // + // Decoding loop. + // + for (SourceIndex = 0; SourceIndex < SourceSize; SourceIndex++) { + SourceChar = Source[SourceIndex]; + + // + // Whitespace is ignored at all positions (regardless of padding mode). + // + if (SourceChar == '\t' || SourceChar == '\n' || SourceChar == '\v' || + SourceChar == '\f' || SourceChar == '\r' || SourceChar == ' ') { + continue; + } + + // + // If we're in padding mode, accept another padding character, as long as + // that padding character completes the quantum. This completes case (2) + // from RFC4648, Chapter 4. "Base 64 Encoding": + // + // (2) The final quantum of encoding input is exactly 8 bits; here, the + // final unit of encoded output will be two characters followed by two + // "=" padding characters. + // + if (PaddingMode) { + if (SourceChar == '=' && SixBitGroupsConsumed == 3) { + SixBitGroupsConsumed = 0; + continue; + } + return RETURN_INVALID_PARAMETER; + } + + // + // When not in padding mode, decode Base64Value based on RFC4648, "Table 1: + // The Base 64 Alphabet". + // + if ('A' <= SourceChar && SourceChar <= 'Z') { + Base64Value = SourceChar - 'A'; + } else if ('a' <= SourceChar && SourceChar <= 'z') { + Base64Value = 26 + (SourceChar - 'a'); + } else if ('0' <= SourceChar && SourceChar <= '9') { + Base64Value = 52 + (SourceChar - '0'); + } else if (SourceChar == '+') { + Base64Value = 62; + } else if (SourceChar == '/') { + Base64Value = 63; + } else if (SourceChar == '=') { + // + // Enter padding mode. + // + PaddingMode = TRUE; + + if (SixBitGroupsConsumed == 2) { + // + // If we have consumed two 6-bit groups from the current quantum before + // encountering the first padding character, then this is case (2) from + // RFC4648, Chapter 4. "Base 64 Encoding". Bump SixBitGroupsConsumed, + // and we'll enforce another padding character. + // + SixBitGroupsConsumed = 3; + } else if (SixBitGroupsConsumed == 3) { + // + // If we have consumed three 6-bit groups from the current quantum + // before encountering the first padding character, then this is case + // (3) from RFC4648, Chapter 4. "Base 64 Encoding". The quantum is now + // complete. + // + SixBitGroupsConsumed = 0; + } else { + // + // Padding characters are not allowed at the first two positions of a + // quantum. + // + return RETURN_INVALID_PARAMETER; + } + + // + // Wherever in a quantum we enter padding mode, we enforce the padding + // bits pending in the accumulator -- from the last 6-bit group just + // preceding the padding character -- to be zero. Refer to RFC4648, + // Chapter 3.5. "Canonical Encoding". + // + if (Accumulator != 0) { + return RETURN_INVALID_PARAMETER; + } + + // + // Advance to the next source character. + // + continue; + } else { + // + // Other characters outside of the encoding alphabet are rejected. + // + return RETURN_INVALID_PARAMETER; + } + + // + // Feed the bits of the current 6-bit group of the quantum to the + // accumulator. + // + Accumulator = (Accumulator << 6) | Base64Value; + SixBitGroupsConsumed++; + switch (SixBitGroupsConsumed) { + case 1: + // + // No octet to spill after consuming the first 6-bit group of the + // quantum; advance to the next source character. + // + continue; + case 2: + // + // 12 bits accumulated (6 pending + 6 new); prepare for spilling an + // octet. 4 bits remain pending. + // + DestinationOctet = (UINT8)(Accumulator >> 4); + Accumulator &= 0xF; + break; + case 3: + // + // 10 bits accumulated (4 pending + 6 new); prepare for spilling an + // octet. 2 bits remain pending. + // + DestinationOctet = (UINT8)(Accumulator >> 2); + Accumulator &= 0x3; + break; + default: + ASSERT (SixBitGroupsConsumed == 4); + // + // 8 bits accumulated (2 pending + 6 new); prepare for spilling an octet. + // The quantum is complete, 0 bits remain pending. + // + DestinationOctet = (UINT8)Accumulator; + Accumulator = 0; + SixBitGroupsConsumed = 0; + break; + } + + // + // Store the decoded octet if there's room left. Increment + // (*DestinationSize) unconditionally. + // + if (*DestinationSize < OriginalDestinationSize) { + ASSERT (Destination != NULL); + Destination[*DestinationSize] = DestinationOctet; + } + (*DestinationSize)++; + + // + // Advance to the next source character. + // + } + + // + // If Source terminates mid-quantum, then Source is invalid. + // + if (SixBitGroupsConsumed != 0) { + return RETURN_INVALID_PARAMETER; + } + + // + // Done. + // + if (*DestinationSize <= OriginalDestinationSize) { + return RETURN_SUCCESS; + } + return RETURN_BUFFER_TOO_SMALL; +} + /** Converts an 8-bit value to an 8-bit BCD value. |