public unsafe override int GetChars(byte* bytes, int byteCount,
char* chars, int charCount, DecoderNLS baseDecoder)
{
// Just need to ASSERT, this is called by something else internal that checked parameters already
Debug.Assert(bytes != null, "[DBCSCodePageEncoding.GetChars]bytes is null");
Debug.Assert(byteCount >= 0, "[DBCSCodePageEncoding.GetChars]byteCount is negative");
Debug.Assert(chars != null, "[DBCSCodePageEncoding.GetChars]chars is null");
Debug.Assert(charCount >= 0, "[DBCSCodePageEncoding.GetChars]charCount is negative");
CheckMemorySection();
// Fix our decoder
DBCSDecoder decoder = (DBCSDecoder)baseDecoder;
// We'll need to know where the end is
byte* byteStart = bytes;
byte* byteEnd = bytes + byteCount;
char* charStart = chars;
char* charEnd = chars + charCount;
bool bUsedDecoder = false;
// Get our fallback
DecoderFallbackBuffer fallbackBuffer = null;
// Shouldn't have anything in fallback buffer for GetChars
Debug.Assert(decoder == null || !decoder.m_throwOnOverflow ||
!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
"[DBCSCodePageEncoding.GetChars]Expected empty fallback buffer at start");
DecoderFallbackBufferHelper fallbackHelper = new DecoderFallbackBufferHelper(fallbackBuffer);
// If we have a left over byte, use it
if (decoder != null && decoder.bLeftOver > 0)
{
// We have a left over byte?
if (byteCount == 0)
{
// No input though
if (!decoder.MustFlush)
{
// Don't have to flush
return 0;
}
// Well, we're flushing, so use '?' or fallback
// fallback leftover byte
Debug.Assert(fallbackBuffer == null,
"[DBCSCodePageEncoding.GetChars]Expected empty fallback");
fallbackBuffer = decoder.FallbackBuffer;
fallbackHelper = new DecoderFallbackBufferHelper(fallbackBuffer);
fallbackHelper.InternalInitialize(bytes, charEnd);
// If no room, it's hopeless, this was 1st fallback
byte[] byteBuffer = new byte[] { unchecked((byte)decoder.bLeftOver) };
if (!fallbackHelper.InternalFallback(byteBuffer, bytes, ref chars))
ThrowCharsOverflow(decoder, true);
decoder.bLeftOver = 0;
// Done, return it
return (int)(chars - charStart);
}
// Get our full info
int iBytes = decoder.bLeftOver << 8;
iBytes |= (*bytes);
bytes++;
// Look up our bytes
char cDecoder = mapBytesToUnicode[iBytes];
if (cDecoder == UNKNOWN_CHAR_FLAG && iBytes != 0)
{
Debug.Assert(fallbackBuffer == null,
"[DBCSCodePageEncoding.GetChars]Expected empty fallback for two bytes");
fallbackBuffer = decoder.FallbackBuffer;
fallbackHelper = new DecoderFallbackBufferHelper(fallbackBuffer);
fallbackHelper.InternalInitialize(byteEnd - byteCount, charEnd);
byte[] byteBuffer = new byte[] { unchecked((byte)(iBytes >> 8)), unchecked((byte)iBytes) };
if (!fallbackHelper.InternalFallback(byteBuffer, bytes, ref chars))
ThrowCharsOverflow(decoder, true);
}
else
{
// Do we have output room?, hopeless if not, this is first char
if (chars >= charEnd)
ThrowCharsOverflow(decoder, true);
*(chars++) = cDecoder;
}
}
// Loop, paying attention to our fallbacks.
while (bytes < byteEnd)
{
// Faster if don't use *bytes++;
int iBytes = *bytes;
bytes++;
char c = mapBytesToUnicode[iBytes];
// See if it was a double byte character
if (c == LEAD_BYTE_CHAR)
{
// Its a lead byte
if (bytes < byteEnd)
{
// Have another to use, so use it
iBytes <<= 8;
iBytes |= *bytes;
bytes++;
c = mapBytesToUnicode[iBytes];
}
else
{
// No input left
if (decoder == null || decoder.MustFlush)
{
// have to flush anyway, set to unknown so we use fallback
c = UNKNOWN_CHAR_FLAG;
}
else
{
// Stick it in decoder
bUsedDecoder = true;
decoder.bLeftOver = (byte)iBytes;
break;
}
}
}
// See if it was unknown
if (c == UNKNOWN_CHAR_FLAG && iBytes != 0)
{
if (fallbackBuffer == null)
{
if (decoder == null)
fallbackBuffer = DecoderFallback.CreateFallbackBuffer();
else
fallbackBuffer = decoder.FallbackBuffer;
fallbackHelper = new DecoderFallbackBufferHelper(fallbackBuffer);
fallbackHelper.InternalInitialize(byteEnd - byteCount, charEnd);
}
// Do fallback
byte[] byteBuffer = null;
if (iBytes < 0x100)
byteBuffer = new byte[] { unchecked((byte)iBytes) };
else
byteBuffer = new byte[] { unchecked((byte)(iBytes >> 8)), unchecked((byte)iBytes) };
if (!fallbackHelper.InternalFallback(byteBuffer, bytes, ref chars))
{
// May or may not throw, but we didn't get these byte(s)
Debug.Assert(bytes >= byteStart + byteBuffer.Length,
"[DBCSCodePageEncoding.GetChars]Expected bytes to have advanced for fallback");
bytes -= byteBuffer.Length; // didn't use these byte(s)
fallbackHelper.InternalReset(); // Didn't fall this back
ThrowCharsOverflow(decoder, bytes == byteStart); // throw?
break; // don't throw, but stop loop
}
}
else
{
// Do we have buffer room?
if (chars >= charEnd)
{
// May or may not throw, but we didn't get these byte(s)
Debug.Assert(bytes > byteStart,
"[DBCSCodePageEncoding.GetChars]Expected bytes to have advanced for lead byte");
bytes--; // unused byte
if (iBytes >= 0x100)
{
Debug.Assert(bytes > byteStart,
"[DBCSCodePageEncoding.GetChars]Expected bytes to have advanced for trail byte");
bytes--; // 2nd unused byte
}
ThrowCharsOverflow(decoder, bytes == byteStart); // throw?
break; // don't throw, but stop loop
}
*(chars++) = c;
}
}
// We already stuck it in encoder if necessary, but we have to clear cases where nothing new got into decoder
if (decoder != null)
{
// Clear it in case of MustFlush
if (bUsedDecoder == false)
{
decoder.bLeftOver = 0;
}
// Remember our count
decoder.m_bytesUsed = (int)(bytes - byteStart);
}
// Shouldn't have anything in fallback buffer for GetChars
Debug.Assert(decoder == null || !decoder.m_throwOnOverflow ||
!decoder.InternalHasFallbackBuffer || decoder.FallbackBuffer.Remaining == 0,
"[DBCSCodePageEncoding.GetChars]Expected empty fallback buffer at end");
// Return length of our output
return (int)(chars - charStart);
}