internal override unsafe 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
BCLDebug.Assert(bytes != null, "[Latin1Encoding.GetBytes]bytes is null");
BCLDebug.Assert(byteCount >= 0, "[Latin1Encoding.GetBytes]byteCount is negative");
BCLDebug.Assert(chars != null, "[Latin1Encoding.GetBytes]chars is null");
BCLDebug.Assert(charCount >= 0, "[Latin1Encoding.GetBytes]charCount is negative");
// Assert because we shouldn't be able to have a null encoder.
BCLDebug.Assert(encoderFallback != null, "[Latin1Encoding.GetBytes]Attempting to use null encoder fallback");
// Get any left over characters & check fast or slower fallback type
char charLeftOver = (char)0;
EncoderReplacementFallback fallback = null;
if (encoder != null)
{
charLeftOver = encoder.charLeftOver;
fallback = encoder.Fallback as EncoderReplacementFallback;
BCLDebug.Assert(charLeftOver == 0 || Char.IsHighSurrogate(charLeftOver),
"[Latin1Encoding.GetBytes]leftover character should be high surrogate");
// Verify that we have no fallbackbuffer, for ASCII its always empty, so just assert
BCLDebug.Assert(!encoder.m_throwOnOverflow || !encoder.InternalHasFallbackBuffer ||
encoder.FallbackBuffer.Remaining == 0,
"[Latin1CodePageEncoding.GetBytes]Expected empty fallback buffer");
}
else
{
fallback = this.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)
{
// Fast version
char cReplacement=fallback.DefaultString[0];
// Check for replacements in range, otherwise fall back to slow version.
if (cReplacement <= (char)0xff)
{
// We should have exactly as many output bytes as input bytes, unless there's a left
// over character, in which case we may need one more.
// If we had a left over character 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 seperate 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++) = (byte)cReplacement;
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;
}
// We just do a quick copy
while (chars < charEnd)
{
char ch2 = *(chars++);
if (ch2 > 0x00ff) *(bytes++) = (byte)cReplacement;
else *(bytes++) = (byte)ch2;
}
// 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.
// prepare our end
byte* byteEnd = bytes + byteCount;
// For fallback we may need a fallback buffer, we know we aren't default fallback, create & init it
EncoderFallbackBuffer fallbackBuffer = null;
// 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
BCLDebug.Assert(encoder != null,
"[Latin1Encoding.GetBytes]Expected encoder if we have charLeftOver");
fallbackBuffer = encoder.FallbackBuffer;
fallbackBuffer.InternalInitialize(chars, charEnd, encoder, true);
// Since left over char was a surrogate, it'll have to be fallen back.
// Get Fallback
// This will fallback a pair if *chars is a low surrogate
fallbackBuffer.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' : fallbackBuffer.InternalGetNextChar()) != 0 ||
chars < charEnd)
{
// First unwind any fallback
if (ch == 0)
{
// No fallback, just get next char
ch = *chars;
chars++;
}
// Check for fallback, this'll catch surrogate pairs too.
// All characters >= 0x100 must fall back.
if (ch > 0xff)
{
// Initialize the buffer
if (fallbackBuffer == null)
{
if (encoder == null)
fallbackBuffer = this.encoderFallback.CreateFallbackBuffer();
else
fallbackBuffer = encoder.FallbackBuffer;
fallbackBuffer.InternalInitialize(charEnd - charCount, charEnd, encoder, true);
}
// Get Fallback
fallbackBuffer.InternalFallback(ch, ref chars);
// Make sure we have enough room. Each fallback char will be 1 output char
// (or else cause a recursion exception)
if (fallbackBuffer.Remaining > byteEnd - bytes)
{
// Didn't use this char, throw it. Chars should've advanced by now
// If we had encoder fallback data it would've thrown before the loop
BCLDebug.Assert(chars > charStart,
"[Latin1Encoding.GetBytes]Expected chars to have advanced (fallback case)");
chars--;
fallbackBuffer.InternalReset();
// Throw it
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
BCLDebug.Assert(fallbackBuffer == null || fallbackBuffer.bFallingBack == false,
"[Latin1Encoding.GetBytes]Expected fallback to have throw initially if insufficient space");
if (fallbackBuffer == null || fallbackBuffer.bFallingBack == false)
{
BCLDebug.Assert(chars > charStart,
"[Latin1Encoding.GetBytes]Expected chars to have advanced (fallback case)");
chars--; // don't use last char
}
ThrowBytesOverflow(encoder, chars == charStart); // throw ?
break; // don't throw, stop
}
// Go ahead and add it
*bytes = unchecked((byte)ch);
bytes++;
}
// Need to do encoder stuff
if (encoder != null)
{
// Fallback stuck it in encoder if necessary, but we have to clear MustFlush cases
if (fallbackBuffer != null && !fallbackBuffer.bUsedEncoder)
// Clear it in case of MustFlush
encoder.charLeftOver = (char)0;
// Set our chars used count
encoder.m_charsUsed = (int)(chars - charStart);
}
BCLDebug.Assert(fallbackBuffer == null || fallbackBuffer.Remaining == 0,
"[Latin1Encoding.GetBytes]Expected Empty fallback buffer");
return (int)(bytes - byteStart);
}