String TextCodecGtk::decode(const char* bytes, size_t length, bool flush, bool stopOnError, bool& sawError)
{
    // Get a converter for the passed-in encoding.
    if (m_iconvDecoder == reinterpret_cast<GIConv>(-1)) {
        createIConvDecoder();
        ASSERT(m_iconvDecoder != reinterpret_cast<GIConv>(-1));
        if (m_iconvDecoder == reinterpret_cast<GIConv>(-1)) {
            LOG_ERROR("Error creating IConv encoder even though encoding was in table.");
            return String();
        }
    }

    size_t countWritten, countRead, conversionLength;
    const char* conversionBytes;
    char* prefixedBytes = 0;

    if (m_numBufferedBytes) {
        conversionLength = length + m_numBufferedBytes;
        prefixedBytes = static_cast<char*>(fastMalloc(conversionLength));
        memcpy(prefixedBytes, m_bufferedBytes, m_numBufferedBytes);
        memcpy(prefixedBytes + m_numBufferedBytes, bytes, length);
        
        conversionBytes = prefixedBytes;
        
        // all buffered bytes are consumed now
        m_numBufferedBytes = 0;
    } else {
        // no previously buffered partial data, 
        // just convert the data that was passed in
        conversionBytes = bytes;
        conversionLength = length;
    }

    GOwnPtr<GError> err;
    GOwnPtr<UChar> buffer;

    buffer.outPtr() = reinterpret_cast<UChar*>(g_convert_with_iconv(conversionBytes, conversionLength, m_iconvDecoder, &countRead, &countWritten, &err.outPtr())); 


    if (err) {
        LOG_ERROR("GIConv conversion error, Code %d: \"%s\"", err->code, err->message);
        m_numBufferedBytes = 0; // reset state for subsequent calls to decode
        fastFree(prefixedBytes);
        sawError = true;
        return String();
    }
    
    // Partial input at the end of the string may not result in an error being raised. 
    // From the gnome library documentation on g_convert_with_iconv:
    // "Even if the conversion was successful, this may be less than len if there were partial characters at the end of the input."
    // That's why we need to compare conversionLength against countRead 

    m_numBufferedBytes = conversionLength - countRead;
    if (m_numBufferedBytes > 0) {
        if (flush) {
            LOG_ERROR("Partial bytes at end of input while flush requested.");
            m_numBufferedBytes = 0; // reset state for subsequent calls to decode
            fastFree(prefixedBytes);
            sawError = true;
            return String();
        }
        memcpy(m_bufferedBytes, conversionBytes + countRead, m_numBufferedBytes);
    }

    fastFree(prefixedBytes);
    
    Vector<UChar> result;

    result.append(buffer.get(), countWritten / sizeof(UChar));

    return String::adopt(result);
}
String TextCodecGtk::decode(const char* bytes, size_t length, bool flush, bool stopOnError, bool& sawError)
{
    // Get a converter for the passed-in encoding.
    if (!m_iconvDecoder)
        createIConvDecoder();
    if (!m_iconvDecoder) {
        LOG_ERROR("Error creating IConv encoder even though encoding was in table.");
        return String();
    }

    Vector<UChar> result;

    gsize bytesRead = 0;
    gsize bytesWritten = 0;
    const gchar* input = bytes;
    gsize inputLength = length;
    gchar buffer[ConversionBufferSize];
    int flags = !length ? G_CONVERTER_INPUT_AT_END : G_CONVERTER_NO_FLAGS;
    if (flush)
        flags |= G_CONVERTER_FLUSH;

    bool bufferWasFull = false;
    char* prefixedBytes = 0;

    if (m_numBufferedBytes) {
        inputLength = length + m_numBufferedBytes;
        prefixedBytes = static_cast<char*>(fastMalloc(inputLength));
        memcpy(prefixedBytes, m_bufferedBytes, m_numBufferedBytes);
        memcpy(prefixedBytes + m_numBufferedBytes, bytes, length);

        input = prefixedBytes;

        // all buffered bytes are consumed now
        m_numBufferedBytes = 0;
    }

    do {
        GOwnPtr<GError> error;
        GConverterResult res = g_converter_convert(G_CONVERTER(m_iconvDecoder.get()),
                                                   input, inputLength,
                                                   buffer, sizeof(buffer),
                                                   static_cast<GConverterFlags>(flags),
                                                   &bytesRead, &bytesWritten,
                                                   &error.outPtr());
        input += bytesRead;
        inputLength -= bytesRead;

        if (res == G_CONVERTER_ERROR) {
            if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT)) {
                // There is not enough input to fully determine what the conversion should produce,
                // save it to a buffer to prepend it to the next input.
                memcpy(m_bufferedBytes, input, inputLength);
                m_numBufferedBytes = inputLength;
                inputLength = 0;
            } else if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_NO_SPACE))
                bufferWasFull = true;
            else if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_INVALID_DATA)) {
                if (stopOnError)
                    sawError = true;
                if (inputLength) {
                    // Ignore invalid character.
                    input += 1;
                    inputLength -= 1;
                }
            } else {
                sawError = true;
                LOG_ERROR("GIConv conversion error, Code %d: \"%s\"", error->code, error->message);
                m_numBufferedBytes = 0; // Reset state for subsequent calls to decode.
                fastFree(prefixedBytes);
                return String();
            }
        }

        result.append(reinterpret_cast<UChar*>(buffer), bytesWritten / sizeof(UChar));
    } while ((inputLength || bufferWasFull) && !sawError);

    fastFree(prefixedBytes);

    return String::adopt(result);
}