SkPDF: alloc less memory for strings
before:
micros bench
250.98 WritePDFText nonrendering
after:
micros bench
107.10 WritePDFText nonrendering
Also, be slightly more space-efficient in encoding strings.
Also, add a bench.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2099463002
Review-Url: https://codereview.chromium.org/2099463002
diff --git a/bench/PDFBench.cpp b/bench/PDFBench.cpp
index 76ee3ca..8f5d2db 100644
--- a/bench/PDFBench.cpp
+++ b/bench/PDFBench.cpp
@@ -208,6 +208,25 @@
}
};
+struct WritePDFTextBenchmark : public Benchmark {
+ std::unique_ptr<SkWStream> fWStream;
+ WritePDFTextBenchmark() : fWStream(new NullWStream) {}
+ const char* onGetName() override { return "WritePDFText"; }
+ bool isSuitableFor(Backend backend) override {
+ return backend == kNonRendering_Backend;
+ }
+ void onDraw(int loops, SkCanvas*) override {
+ static const char kHello[] = "HELLO SKIA!\n";
+ static const char kBinary[] = "\001\002\003\004\005\006";
+ while (loops-- > 0) {
+ for (int i = 1000; i-- > 0;) {
+ SkPDFUtils::WriteString(fWStream.get(), kHello, strlen(kHello));
+ SkPDFUtils::WriteString(fWStream.get(), kBinary, strlen(kBinary));
+ }
+ }
+ }
+};
+
} // namespace
DEF_BENCH(return new PDFImageBench;)
DEF_BENCH(return new PDFJpegImageBench;)
@@ -215,3 +234,4 @@
DEF_BENCH(return new PDFScalarBench;)
DEF_BENCH(return new PDFShaderBench;)
DEF_BENCH(return new WStreamWriteTextBenchmark;)
+DEF_BENCH(return new WritePDFTextBenchmark;)
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index 5b4ae93..b9e310d 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -1067,30 +1067,32 @@
// discarded. If true, the upper byte is encoded
// first. Otherwise, we assert the upper byte is
// zero.
-static SkString format_wide_string(const uint16_t* input,
- size_t len,
- bool wideChars) {
+static void write_wide_string(SkDynamicMemoryWStream* wStream,
+ const uint16_t* input,
+ size_t len,
+ bool wideChars) {
if (wideChars) {
SkASSERT(2 * len < 65535);
static const char gHex[] = "0123456789ABCDEF";
- SkString result(4 * len + 2);
- result[0] = '<';
+ wStream->writeText("<");
for (size_t i = 0; i < len; i++) {
- result[4 * i + 1] = gHex[(input[i] >> 12) & 0xF];
- result[4 * i + 2] = gHex[(input[i] >> 8) & 0xF];
- result[4 * i + 3] = gHex[(input[i] >> 4) & 0xF];
- result[4 * i + 4] = gHex[(input[i] ) & 0xF];
+ char result[4]; // Big-endian
+ result[0] = gHex[(input[i] >> 12) & 0xF];
+ result[1] = gHex[(input[i] >> 8) & 0xF];
+ result[2] = gHex[(input[i] >> 4) & 0xF];
+ result[3] = gHex[(input[i]) & 0xF];
+ wStream->write(result, 4);
}
- result[4 * len + 1] = '>';
- return result;
+ wStream->writeText(">");
} else {
SkASSERT(len <= 65535);
- SkString tmp(len);
+ SkAutoMalloc buffer(len); // Remove every other byte.
+ uint8_t* ptr = (uint8_t*)buffer.get();
for (size_t i = 0; i < len; i++) {
SkASSERT(0 == input[i] >> 8);
- tmp[i] = static_cast<uint8_t>(input[i]);
+ ptr[i] = static_cast<uint8_t>(input[i]);
}
- return SkPDFUtils::FormatString(tmp.c_str(), tmp.size());
+ SkPDFUtils::WriteString(wStream, (char*)buffer.get(), len);
}
}
@@ -1184,10 +1186,9 @@
fFontGlyphUsage->noteGlyphUsage(
font, glyphIDsCopy.begin() + consumedGlyphCount,
availableGlyphs);
- SkString encodedString =
- format_wide_string(glyphIDsCopy.begin() + consumedGlyphCount,
- availableGlyphs, font->multiByteGlyphs());
- content.entry()->fContent.writeText(encodedString.c_str());
+ write_wide_string(&content.entry()->fContent,
+ glyphIDsCopy.begin() + consumedGlyphCount,
+ availableGlyphs, font->multiByteGlyphs());
consumedGlyphCount += availableGlyphs;
content.entry()->fContent.writeText(" Tj\n");
}
@@ -1264,9 +1265,8 @@
align_text(glyphCacheProc, textPaint, glyphIDs + i, 1, &x, &y);
set_text_transform(x, y, textPaint.getTextSkewX(), &content.entry()->fContent);
- SkString encodedString =
- format_wide_string(&encodedValue, 1, font->multiByteGlyphs());
- content.entry()->fContent.writeText(encodedString.c_str());
+ write_wide_string(&content.entry()->fContent, &encodedValue, 1,
+ font->multiByteGlyphs());
content.entry()->fContent.writeText(" Tj\n");
}
content.entry()->fContent.writeText("ET\n");
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index 285da38..19635ab 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -110,18 +110,6 @@
}
}
-static void write_string(SkWStream* o, const SkString& s) {
- o->write(s.c_str(), s.size());
-}
-
-static SkString format_string(const SkString& s) {
- return SkPDFUtils::FormatString(s.c_str(), s.size());
-}
-
-static SkString format_string(const char* s) {
- return SkPDFUtils::FormatString(s, strlen(s));
-}
-
void SkPDFUnion::emitObject(SkWStream* stream,
const SkPDFObjNumMap& objNumMap,
const SkPDFSubstituteMap& substitutes) const {
@@ -142,14 +130,16 @@
return;
case Type::kString:
SkASSERT(fStaticString);
- write_string(stream, format_string(fStaticString));
+ SkPDFUtils::WriteString(stream, fStaticString,
+ strlen(fStaticString));
return;
case Type::kNameSkS:
stream->writeText("/");
write_name_escaped(stream, pun(fSkString)->c_str());
return;
case Type::kStringSkS:
- write_string(stream, format_string(*pun(fSkString)));
+ SkPDFUtils::WriteString(stream, pun(fSkString)->c_str(),
+ pun(fSkString)->size());
return;
case Type::kObjRef:
stream->writeDecAsText(objNumMap.getObjectNumber(
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
index f305765..4e1a6d7 100644
--- a/src/pdf/SkPDFUtils.cpp
+++ b/src/pdf/SkPDFUtils.cpp
@@ -370,46 +370,48 @@
return output - result;
}
-SkString SkPDFUtils::FormatString(const char* cin, size_t len) {
+void SkPDFUtils::WriteString(SkWStream* wStream, const char* cin, size_t len) {
SkDEBUGCODE(static const size_t kMaxLen = 65535;)
SkASSERT(len <= kMaxLen);
- // 7-bit clean is a heuristic to decide what string format to use;
- // a 7-bit clean string should require little escaping.
- bool sevenBitClean = true;
- size_t characterCount = 2 + len;
+ size_t extraCharacterCount = 0;
for (size_t i = 0; i < len; i++) {
if (cin[i] > '~' || cin[i] < ' ') {
- sevenBitClean = false;
- break;
+ extraCharacterCount += 3;
}
if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
- ++characterCount;
+ ++extraCharacterCount;
}
}
- SkString result;
- if (sevenBitClean) {
- result.resize(characterCount);
- char* str = result.writable_str();
- *str++ = '(';
+ if (extraCharacterCount <= len) {
+ wStream->writeText("(");
for (size_t i = 0; i < len; i++) {
- if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
- *str++ = '\\';
+ if (cin[i] > '~' || cin[i] < ' ') {
+ uint8_t c = static_cast<uint8_t>(cin[i]);
+ uint8_t octal[4];
+ octal[0] = '\\';
+ octal[1] = '0' + ( c >> 6 );
+ octal[2] = '0' + ((c >> 3) & 0x07);
+ octal[3] = '0' + ( c & 0x07);
+ wStream->write(octal, 4);
+ } else {
+ if (cin[i] == '\\' || cin[i] == '(' || cin[i] == ')') {
+ wStream->writeText("\\");
+ }
+ wStream->write(&cin[i], 1);
}
- *str++ = cin[i];
}
- *str++ = ')';
+ wStream->writeText(")");
} else {
- result.resize(2 * len + 2);
- char* str = result.writable_str();
- *str++ = '<';
+ wStream->writeText("<");
for (size_t i = 0; i < len; i++) {
uint8_t c = static_cast<uint8_t>(cin[i]);
static const char gHex[] = "0123456789ABCDEF";
- *str++ = gHex[(c >> 4) & 0xF];
- *str++ = gHex[(c ) & 0xF];
+ char hexValue[2];
+ hexValue[0] = gHex[(c >> 4) & 0xF];
+ hexValue[1] = gHex[ c & 0xF];
+ wStream->write(hexValue, 2);
}
- *str++ = '>';
+ wStream->writeText(">");
}
- return result;
}
diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h
index 3340089..693d9cd 100644
--- a/src/pdf/SkPDFUtils.h
+++ b/src/pdf/SkPDFUtils.h
@@ -66,7 +66,7 @@
static size_t FloatToDecimal(float value,
char output[kMaximumFloatDecimalLength]);
static void AppendScalar(SkScalar value, SkWStream* stream);
- static SkString FormatString(const char* input, size_t len);
+ static void WriteString(SkWStream* wStream, const char* input, size_t len);
};
#endif
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index e438bfe..9e2a89e 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -217,9 +217,11 @@
SkString stringComplexInput("\ttest ) string ( foo");
SkPDFUnion stringComplex = SkPDFUnion::String(stringComplexInput);
- ASSERT_EMIT_EQ(reporter,
- stringComplex,
- "<0974657374202920737472696E67202820666F6F>");
+ ASSERT_EMIT_EQ(reporter, stringComplex, "(\\011test \\) string \\( foo)");
+
+ SkString binaryStringInput("\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20");
+ SkPDFUnion binaryString = SkPDFUnion::String(binaryStringInput);
+ ASSERT_EMIT_EQ(reporter, binaryString, "<0102030405060708090A0B0C0D0E0F10>");
SkString nameInput("Test name\twith#tab");
SkPDFUnion name = SkPDFUnion::Name(nameInput);