public unsafe override int GetBytes(char* chars, int charCount,
byte* bytes, int byteCount, EncoderNLS encoder)
{
// Just need to ASSERT, this is called by something else internal that checked parameters already
Debug.Assert(bytes != null, "[SBCSCodePageEncoding.GetBytes]bytes is null");
Debug.Assert(byteCount >= 0, "[SBCSCodePageEncoding.GetBytes]byteCount is negative");
Debug.Assert(chars != null, "[SBCSCodePageEncoding.GetBytes]chars is null");
Debug.Assert(charCount >= 0, "[SBCSCodePageEncoding.GetBytes]charCount is negative");
// Assert because we shouldn't be able to have a null encoder.
Debug.Assert(EncoderFallback != null, "[SBCSCodePageEncoding.GetBytes]Attempting to use null encoder fallback");
CheckMemorySection();
// Need to test fallback
EncoderReplacementFallback fallback = null;
// Get any left over characters
char charLeftOver = (char)0;
if (encoder != null)
{
charLeftOver = encoder.charLeftOver;
Debug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver),
"[SBCSCodePageEncoding.GetBytes]leftover character should be high surrogate");
fallback = encoder.Fallback as EncoderReplacementFallback;
// Verify that we have no fallbackbuffer, for SBCS its always empty, so just assert
Debug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer ||
encoder.FallbackBuffer.Remaining == 0,
"[SBCSCodePageEncoding.GetBytes]Expected empty fallback buffer at start");
// if (encoder.m_throwOnOverflow && encoder.InternalHasFallbackBuffer &&
// encoder.FallbackBuffer.Remaining > 0)
// throw new ArgumentException(Environment.GetResourceString("Argument_EncoderFallbackNotEmpty",
// EncodingName, encoder.Fallback.GetType()));
}
else
{
// If we aren't using default fallback then we may have a complicated count.
fallback = EncoderFallback as EncoderReplacementFallback;
}
// prepare our end
char* charEnd = chars + charCount;
byte* byteStart = bytes;
char* charStart = chars;
// See if we do the fast default or slightly slower fallback
if (fallback != null && fallback.MaxCharCount == 1)
{
// Make sure our fallback character is valid first
byte bReplacement = _mapUnicodeToBytes[fallback.DefaultString[0]];
// Check for replacements in range, otherwise fall back to slow version.
if (bReplacement != 0)
{
// We should have exactly as many output bytes as input bytes, unless there's a leftover
// character, in which case we may need one more.
// If we had a leftover character we will have to add a ? (This happens if they had a funky
// fallback last time, but not this time. We can't spit any out though,
// because with fallback encoder each surrogate is treated as a separate code point)
if (charLeftOver > 0)
{
// Have to have room
// Throw even if doing no throw version because this is just 1 char,
// so buffer will never be big enough
if (byteCount == 0)
ThrowBytesOverflow(encoder, true);
// This'll make sure we still have more room and also make sure our return value is correct.
*(bytes++) = bReplacement;
byteCount--; // We used one of the ones we were counting.
}
// This keeps us from overrunning our output buffer
if (byteCount < charCount)
{
// Throw or make buffer smaller?
ThrowBytesOverflow(encoder, byteCount < 1);
// Just use what we can
charEnd = chars + byteCount;
}
// Simple way
while (chars < charEnd)
{
char ch2 = *chars;
chars++;
byte bTemp = _mapUnicodeToBytes[ch2];
// Check for fallback
if (bTemp == 0 && ch2 != (char)0)
*bytes = bReplacement;
else
*bytes = bTemp;
bytes++;
}
// Clear encoder
if (encoder != null)
{
encoder.charLeftOver = (char)0;
encoder.m_charsUsed = (int)(chars - charStart);
}
return (int)(bytes - byteStart);
}
}
// Slower version, have to do real fallback.
// For fallback we may need a fallback buffer, we know we aren't default fallback
EncoderFallbackBuffer fallbackBuffer = null;
// prepare our end
byte* byteEnd = bytes + byteCount;
EncoderFallbackBufferHelper fallbackHelper = new EncoderFallbackBufferHelper(fallbackBuffer);
// We may have a left over character from last time, try and process it.
if (charLeftOver > 0)
{
// Since left over char was a surrogate, it'll have to be fallen back.
// Get Fallback
Debug.Assert(encoder != null, "[SBCSCodePageEncoding.GetBytes]Expect to have encoder if we have a charLeftOver");
fallbackBuffer = encoder.FallbackBuffer;
fallbackHelper = new EncoderFallbackBufferHelper(fallbackBuffer);
fallbackHelper.InternalInitialize(chars, charEnd, encoder, true);
// This will fallback a pair if *chars is a low surrogate
fallbackHelper.InternalFallback(charLeftOver, ref chars);
if (fallbackBuffer.Remaining > byteEnd - bytes)
{
// Throw it, if we don't have enough for this we never will
ThrowBytesOverflow(encoder, true);
}
}
// Now we may have fallback char[] already from the encoder fallback above
// Go ahead and do it, including the fallback.
char ch;
while ((ch = (fallbackBuffer == null) ? '\0' : fallbackHelper.InternalGetNextChar()) != 0 ||
chars < charEnd)
{
// First unwind any fallback
if (ch == 0)
{
// No fallback, just get next char
ch = *chars;
chars++;
}
// get byte for this char
byte bTemp = _mapUnicodeToBytes[ch];
// Check for fallback, this'll catch surrogate pairs too.
if (bTemp == 0 && ch != (char)0)
{
// Get Fallback
if (fallbackBuffer == null)
{
// Create & init fallback buffer
if (encoder == null)
fallbackBuffer = EncoderFallback.CreateFallbackBuffer();
else
fallbackBuffer = encoder.FallbackBuffer;
fallbackHelper = new EncoderFallbackBufferHelper(fallbackBuffer);
// chars has moved so we need to remember figure it out so Exception fallback
// index will be correct
fallbackHelper.InternalInitialize(charEnd - charCount, charEnd, encoder, true);
}
// Make sure we have enough room. Each fallback char will be 1 output char
// (or recursion exception will be thrown)
fallbackHelper.InternalFallback(ch, ref chars);
if (fallbackBuffer.Remaining > byteEnd - bytes)
{
// Didn't use this char, reset it
Debug.Assert(chars > charStart, "[SBCSCodePageEncoding.GetBytes]Expected chars to have advanced (fallback)");
chars--;
fallbackHelper.InternalReset();
// Throw it & drop this data
ThrowBytesOverflow(encoder, chars == charStart);
break;
}
continue;
}
// We'll use this one
// Bounds check
if (bytes >= byteEnd)
{
// didn't use this char, we'll throw or use buffer
Debug.Assert(fallbackBuffer == null || fallbackHelper.bFallingBack == false, "[SBCSCodePageEncoding.GetBytes]Expected to NOT be falling back");
if (fallbackBuffer == null || fallbackHelper.bFallingBack == false)
{
Debug.Assert(chars > charStart, "[SBCSCodePageEncoding.GetBytes]Expected chars to have advanced (normal)");
chars--; // don't use last char
}
ThrowBytesOverflow(encoder, chars == charStart); // throw ?
break; // don't throw, stop
}
// Go ahead and add it
*bytes = bTemp;
bytes++;
}
// encoder stuff if we have one
if (encoder != null)
{
// Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases
if (fallbackBuffer != null && !fallbackHelper.bUsedEncoder)
// Clear it in case of MustFlush
encoder.charLeftOver = (char)0;
// Set our chars used count
encoder.m_charsUsed = (int)(chars - charStart);
}
// Expect Empty fallback buffer for SBCS
Debug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0, "[SBCSEncoding.GetBytes]Expected Empty fallback buffer at end");
return (int)(bytes - byteStart);
}