IN OUT UINTN *DestinationSize\r
)\r
{\r
- ASSERT (FALSE);\r
- return RETURN_INVALID_PARAMETER;\r
+ BOOLEAN PaddingMode;\r
+ UINTN SixBitGroupsConsumed;\r
+ UINT32 Accumulator;\r
+ UINTN OriginalDestinationSize;\r
+ UINTN SourceIndex;\r
+\r
+ if (DestinationSize == NULL) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check Source array validity.\r
+ //\r
+ if (Source == NULL) {\r
+ if (SourceSize > 0) {\r
+ //\r
+ // At least one CHAR8 element at NULL Source.\r
+ //\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ } else if (SourceSize > MAX_ADDRESS - (UINTN)Source) {\r
+ //\r
+ // Non-NULL Source, but it wraps around.\r
+ //\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check Destination array validity.\r
+ //\r
+ if (Destination == NULL) {\r
+ if (*DestinationSize > 0) {\r
+ //\r
+ // At least one UINT8 element at NULL Destination.\r
+ //\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ } else if (*DestinationSize > MAX_ADDRESS - (UINTN)Destination) {\r
+ //\r
+ // Non-NULL Destination, but it wraps around.\r
+ //\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Check for overlap.\r
+ //\r
+ if (Source != NULL && Destination != NULL) {\r
+ //\r
+ // Both arrays have been provided, and we know from earlier that each array\r
+ // is valid in itself.\r
+ //\r
+ if ((UINTN)Source + SourceSize <= (UINTN)Destination) {\r
+ //\r
+ // Source array precedes Destination array, OK.\r
+ //\r
+ } else if ((UINTN)Destination + *DestinationSize <= (UINTN)Source) {\r
+ //\r
+ // Destination array precedes Source array, OK.\r
+ //\r
+ } else {\r
+ //\r
+ // Overlap.\r
+ //\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Decoding loop setup.\r
+ //\r
+ PaddingMode = FALSE;\r
+ SixBitGroupsConsumed = 0;\r
+ Accumulator = 0;\r
+ OriginalDestinationSize = *DestinationSize;\r
+ *DestinationSize = 0;\r
+\r
+ //\r
+ // Decoding loop.\r
+ //\r
+ for (SourceIndex = 0; SourceIndex < SourceSize; SourceIndex++) {\r
+ CHAR8 SourceChar;\r
+ UINT32 Base64Value;\r
+ UINT8 DestinationOctet;\r
+\r
+ SourceChar = Source[SourceIndex];\r
+\r
+ //\r
+ // Whitespace is ignored at all positions (regardless of padding mode).\r
+ //\r
+ if (SourceChar == '\t' || SourceChar == '\n' || SourceChar == '\v' ||\r
+ SourceChar == '\f' || SourceChar == '\r' || SourceChar == ' ') {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // If we're in padding mode, accept another padding character, as long as\r
+ // that padding character completes the quantum. This completes case (2)\r
+ // from RFC4648, Chapter 4. "Base 64 Encoding":\r
+ //\r
+ // (2) The final quantum of encoding input is exactly 8 bits; here, the\r
+ // final unit of encoded output will be two characters followed by two\r
+ // "=" padding characters.\r
+ //\r
+ if (PaddingMode) {\r
+ if (SourceChar == '=' && SixBitGroupsConsumed == 3) {\r
+ SixBitGroupsConsumed = 0;\r
+ continue;\r
+ }\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // When not in padding mode, decode Base64Value based on RFC4648, "Table 1:\r
+ // The Base 64 Alphabet".\r
+ //\r
+ if ('A' <= SourceChar && SourceChar <= 'Z') {\r
+ Base64Value = SourceChar - 'A';\r
+ } else if ('a' <= SourceChar && SourceChar <= 'z') {\r
+ Base64Value = 26 + (SourceChar - 'a');\r
+ } else if ('0' <= SourceChar && SourceChar <= '9') {\r
+ Base64Value = 52 + (SourceChar - '0');\r
+ } else if (SourceChar == '+') {\r
+ Base64Value = 62;\r
+ } else if (SourceChar == '/') {\r
+ Base64Value = 63;\r
+ } else if (SourceChar == '=') {\r
+ //\r
+ // Enter padding mode.\r
+ //\r
+ PaddingMode = TRUE;\r
+\r
+ if (SixBitGroupsConsumed == 2) {\r
+ //\r
+ // If we have consumed two 6-bit groups from the current quantum before\r
+ // encountering the first padding character, then this is case (2) from\r
+ // RFC4648, Chapter 4. "Base 64 Encoding". Bump SixBitGroupsConsumed,\r
+ // and we'll enforce another padding character.\r
+ //\r
+ SixBitGroupsConsumed = 3;\r
+ } else if (SixBitGroupsConsumed == 3) {\r
+ //\r
+ // If we have consumed three 6-bit groups from the current quantum\r
+ // before encountering the first padding character, then this is case\r
+ // (3) from RFC4648, Chapter 4. "Base 64 Encoding". The quantum is now\r
+ // complete.\r
+ //\r
+ SixBitGroupsConsumed = 0;\r
+ } else {\r
+ //\r
+ // Padding characters are not allowed at the first two positions of a\r
+ // quantum.\r
+ //\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Wherever in a quantum we enter padding mode, we enforce the padding\r
+ // bits pending in the accumulator -- from the last 6-bit group just\r
+ // preceding the padding character -- to be zero. Refer to RFC4648,\r
+ // Chapter 3.5. "Canonical Encoding".\r
+ //\r
+ if (Accumulator != 0) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Advance to the next source character.\r
+ //\r
+ continue;\r
+ } else {\r
+ //\r
+ // Other characters outside of the encoding alphabet are rejected.\r
+ //\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Feed the bits of the current 6-bit group of the quantum to the\r
+ // accumulator.\r
+ //\r
+ Accumulator = (Accumulator << 6) | Base64Value;\r
+ SixBitGroupsConsumed++;\r
+ switch (SixBitGroupsConsumed) {\r
+ case 1:\r
+ //\r
+ // No octet to spill after consuming the first 6-bit group of the\r
+ // quantum; advance to the next source character.\r
+ //\r
+ continue;\r
+ case 2:\r
+ //\r
+ // 12 bits accumulated (6 pending + 6 new); prepare for spilling an\r
+ // octet. 4 bits remain pending.\r
+ //\r
+ DestinationOctet = (UINT8)(Accumulator >> 4);\r
+ Accumulator &= 0xF;\r
+ break;\r
+ case 3:\r
+ //\r
+ // 10 bits accumulated (4 pending + 6 new); prepare for spilling an\r
+ // octet. 2 bits remain pending.\r
+ //\r
+ DestinationOctet = (UINT8)(Accumulator >> 2);\r
+ Accumulator &= 0x3;\r
+ break;\r
+ default:\r
+ ASSERT (SixBitGroupsConsumed == 4);\r
+ //\r
+ // 8 bits accumulated (2 pending + 6 new); prepare for spilling an octet.\r
+ // The quantum is complete, 0 bits remain pending.\r
+ //\r
+ DestinationOctet = (UINT8)Accumulator;\r
+ Accumulator = 0;\r
+ SixBitGroupsConsumed = 0;\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Store the decoded octet if there's room left. Increment\r
+ // (*DestinationSize) unconditionally.\r
+ //\r
+ if (*DestinationSize < OriginalDestinationSize) {\r
+ ASSERT (Destination != NULL);\r
+ Destination[*DestinationSize] = DestinationOctet;\r
+ }\r
+ (*DestinationSize)++;\r
+\r
+ //\r
+ // Advance to the next source character.\r
+ //\r
+ }\r
+\r
+ //\r
+ // If Source terminates mid-quantum, then Source is invalid.\r
+ //\r
+ if (SixBitGroupsConsumed != 0) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Done.\r
+ //\r
+ if (*DestinationSize <= OriginalDestinationSize) {\r
+ return RETURN_SUCCESS;\r
+ }\r
+ return RETURN_BUFFER_TOO_SMALL;\r
}\r
\r
/**\r