+void
+ConversionTest::TestUTF8ToUTF8Overflow() {
+ IcuTestErrorCode errorCode(*this, "TestUTF8ToUTF8Overflow");
+ LocalUConverterPointer cnv1(ucnv_open("UTF-8", errorCode));
+ LocalUConverterPointer cnv2(ucnv_open("UTF-8", errorCode));
+ static const char *text = "aä"; // ä: 2 bytes
+ const char *source = text;
+ const char *sourceLimit = text + strlen(text);
+ char result[20];
+ char *target = result;
+ const char *targetLimit = result + sizeof(result);
+ UChar buffer16[20];
+ UChar *pivotSource = buffer16;
+ UChar *pivotTarget = buffer16;
+ const UChar *pivotLimit = buffer16 + UPRV_LENGTHOF(buffer16);
+ int32_t length;
+
+ // Convert with insufficient target capacity.
+ result[2] = 5;
+ ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
+ &target, result + 2, &source, sourceLimit,
+ buffer16, &pivotSource, &pivotTarget, pivotLimit,
+ FALSE, FALSE, errorCode);
+ assertEquals("overflow", U_BUFFER_OVERFLOW_ERROR, errorCode.reset());
+ length = (int32_t)(target - result);
+ assertEquals("number of bytes written", 2, length);
+ assertEquals("next byte not clobbered", 5, result[2]);
+
+ // Convert the rest and flush.
+ ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
+ &target, targetLimit, &source, sourceLimit,
+ buffer16, &pivotSource, &pivotTarget, pivotLimit,
+ FALSE, TRUE, errorCode);
+
+ assertSuccess("UTF-8->UTF-8", errorCode);
+ length = (int32_t)(target - result);
+ assertEquals("3 bytes", 3, length);
+ if (length == 3) {
+ assertTrue("result same as input", memcmp(text, result, length) == 0);
+ }
+
+ ucnv_reset(cnv1.getAlias());
+ ucnv_reset(cnv2.getAlias());
+ memset(result, 0, sizeof(result));
+ static const char *text2 = "a🚲"; // U+1F6B2 bicycle: 4 bytes
+ source = text2;
+ sourceLimit = text2 + strlen(text2);
+ target = result;
+ pivotSource = pivotTarget = buffer16;
+
+ // Convert with insufficient target capacity.
+ result[3] = 5;
+ ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
+ &target, result + 3, &source, sourceLimit,
+ buffer16, &pivotSource, &pivotTarget, pivotLimit,
+ FALSE, FALSE, errorCode);
+ assertEquals("text2 overflow", U_BUFFER_OVERFLOW_ERROR, errorCode.reset());
+ length = (int32_t)(target - result);
+ assertEquals("text2 number of bytes written", 3, length);
+ assertEquals("text2 next byte not clobbered", 5, result[3]);
+
+ // Convert the rest and flush.
+ ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
+ &target, targetLimit, &source, sourceLimit,
+ buffer16, &pivotSource, &pivotTarget, pivotLimit,
+ FALSE, TRUE, errorCode);
+
+ assertSuccess("text2 UTF-8->UTF-8", errorCode);
+ length = (int32_t)(target - result);
+ assertEquals("text2 5 bytes", 5, length);
+ if (length == 5) {
+ assertTrue("text2 result same as input", memcmp(text2, result, length) == 0);
+ }
+
+ ucnv_reset(cnv1.getAlias());
+ ucnv_reset(cnv2.getAlias());
+ memset(result, 0, sizeof(result));
+ static const char *illFormed = "\xf1\x91\x93\x96\x91\x94"; // U+514D6 + two more trail bytes
+ source = illFormed;
+ sourceLimit = illFormed + strlen(illFormed);
+ target = result;
+ pivotSource = pivotTarget = buffer16;
+
+ ucnv_setToUCallBack(cnv1.getAlias(), UCNV_TO_U_CALLBACK_STOP, nullptr, nullptr, nullptr, errorCode);
+
+ // Convert only two bytes and flush (but expect failure).
+ char errorBytes[10];
+ int8_t errorLength;
+ result[0] = 5;
+ ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
+ &target, targetLimit, &source, source + 2,
+ buffer16, &pivotSource, &pivotTarget, pivotLimit,
+ FALSE, TRUE, errorCode);
+ assertEquals("illFormed truncated", U_TRUNCATED_CHAR_FOUND, errorCode.reset());
+ length = (int32_t)(target - result);
+ assertEquals("illFormed number of bytes written", 0, length);
+ errorLength = UPRV_LENGTHOF(errorBytes);
+ ucnv_getInvalidChars(cnv1.getAlias(), errorBytes, &errorLength, errorCode);
+ assertEquals("illFormed truncated errorLength", 2, (int32_t)errorLength);
+ if (errorLength == 2) {
+ assertEquals("illFormed truncated errorBytes", 0xf191,
+ ((int32_t)(uint8_t)errorBytes[0] << 8) | (uint8_t)errorBytes[1]);
+ }
+
+ // Continue conversion starting with a trail byte.
+ ucnv_convertEx(cnv2.getAlias(), cnv1.getAlias(),
+ &target, targetLimit, &source, sourceLimit,
+ buffer16, &pivotSource, &pivotTarget, pivotLimit,
+ FALSE, TRUE, errorCode);
+
+ assertEquals("illFormed trail byte", U_ILLEGAL_CHAR_FOUND, errorCode.reset());
+ length = (int32_t)(target - result);
+ assertEquals("illFormed trail byte number of bytes written", 0, length);
+ errorLength = UPRV_LENGTHOF(errorBytes);
+ ucnv_getInvalidChars(cnv1.getAlias(), errorBytes, &errorLength, errorCode);
+ assertEquals("illFormed trail byte errorLength", 1, (int32_t)errorLength);
+ if (errorLength == 1) {
+ assertEquals("illFormed trail byte errorBytes", 0x93, (int32_t)(uint8_t)errorBytes[0]);
+ }
+}
+