From d7bfcaf57c1fe1d84880e0d31fab9c3d303cdb79 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Thu, 14 Jun 2018 18:03:26 -0400 Subject: [skjson] Some short string optimizations 1) skip redundant \0 terminator => Value is always zero-initialized 2) skip storing a len record => strlen is cheap for short strings Change-Id: I3c10c9b9cf6155b95124e2c0194c59e9531a7ca4 Reviewed-on: https://skia-review.googlesource.com/135049 Reviewed-by: Mike Klein Commit-Queue: Florin Malita --- modules/skjson/include/SkJSON.h | 19 ++++++++++--------- modules/skjson/src/SkJSON.cpp | 23 +++++++---------------- 2 files changed, 17 insertions(+), 25 deletions(-) (limited to 'modules') diff --git a/modules/skjson/include/SkJSON.h b/modules/skjson/include/SkJSON.h index 1b46c4a100..8baf9fb8fc 100644 --- a/modules/skjson/include/SkJSON.h +++ b/modules/skjson/include/SkJSON.h @@ -12,6 +12,8 @@ #include "SkTo.h" #include "SkTypes.h" +#include + class SkString; class SkWStream; @@ -282,7 +284,11 @@ public: size_t size() const { switch (this->getTag()) { case Tag::kShortString: - return kMaxInlineStringSize - SkToSizeT(this->cast()[kMaxInlineStringSize]); + // We don't bother storing a length for short strings on the assumption + // that strlen is fast in this case. If this becomes problematic, we + // can either go back to storing (7-len) in the tag byte or write a fast + // short_strlen. + return strlen(this->cast()); case Tag::kString: return this->cast>()->size(); default: @@ -297,15 +303,10 @@ public: } const char* end() const { - if (this->getTag() == Tag::kShortString) { - const auto* payload = this->cast(); - return payload + kMaxInlineStringSize - SkToSizeT(payload[kMaxInlineStringSize]); - } - return this->cast>()->end(); + return this->getTag() == Tag::kShortString + ? strchr(this->cast(), '\0') + : this->cast>()->end(); } - -private: - static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 1; }; struct Member { diff --git a/modules/skjson/src/SkJSON.cpp b/modules/skjson/src/SkJSON.cpp index d6585e098c..7f082d7294 100644 --- a/modules/skjson/src/SkJSON.cpp +++ b/modules/skjson/src/SkJSON.cpp @@ -7,6 +7,7 @@ #include "SkJSON.h" +#include "SkMalloc.h" #include "SkStream.h" #include "SkString.h" @@ -82,12 +83,9 @@ static void* MakeVector(const void* src, size_t size, SkArenaAlloc& alloc) { // The Ts are already in memory, so their size should be safe. const auto total_size = sizeof(size_t) + size * sizeof(T) + extra_alloc_size; auto* size_ptr = reinterpret_cast(alloc.makeBytesAlignedTo(total_size, kRecAlign)); - *size_ptr = size; - if (size) { - auto* data_ptr = reinterpret_cast(size_ptr + 1); - memcpy(data_ptr, src, size * sizeof(T)); - } + *size_ptr = size; + sk_careful_memcpy(size_ptr + 1, src, size * sizeof(T)); return size_ptr; } @@ -112,6 +110,7 @@ ArrayValue::ArrayValue(const Value* src, size_t size, SkArenaAlloc& alloc) { // The string data plus a null-char terminator are copied over. // StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) { + static constexpr size_t kMaxInlineStringSize = sizeof(Value) - 1; if (size > kMaxInlineStringSize) { this->init_tagged_pointer(Tag::kString, MakeVector(src, size, alloc)); @@ -122,19 +121,11 @@ StringValue::StringValue(const char* src, size_t size, SkArenaAlloc& alloc) { } this->init_tagged(Tag::kShortString); + sk_careful_memcpy(this->cast(), src, size); - auto* payload = this->cast(); - if (size) { - memcpy(payload, src, size); - payload[size] = '\0'; - } - - const auto len_tag = SkTo(kMaxInlineStringSize - size); - // This technically overwrites the tag, but is safe because - // 1) kShortString == 0 - // 2) 0 <= len_tag <= 7 + // Null terminator provided by init_tagged() above (fData8 is zero-initialized). + // This is safe because kShortString is also 0 and can act as a terminator when size == 7. static_assert(static_cast(Tag::kShortString) == 0, "please don't break this"); - payload[kMaxInlineStringSize] = len_tag; SkASSERT(this->getTag() == Tag::kShortString); } -- cgit v1.2.3