[vm] Serialize strings compactly, instead of as heap images.

Avoids the space overhead for the class, hash and alignment padding.

Flutter Gallery ARM64 release
Total(CodeSize): 8537094 -> 8088430 (-5.25%)

Change-Id: I73bc984f157042824c29efc42d45547914bd3c1b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/160183
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index a973ce7..c31e1a2c 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4847,10 +4847,8 @@
       OneByteStringPtr str = objects_[i];
       AutoTraceObject(str);
       const intptr_t length = Smi::Value(str->ptr()->length_);
-      s->WriteUnsigned(length);
-      s->Write<bool>(str->ptr()->IsCanonical());
-      intptr_t hash = String::GetCachedHash(str);
-      s->Write<int32_t>(hash);
+      ASSERT(length <= compiler::target::kSmiMax);
+      s->WriteUnsigned((length << 1) | (str->ptr()->IsCanonical() ? 1 : 0));
       s->WriteBytes(str->ptr()->data(), length);
     }
   }
@@ -4880,16 +4878,20 @@
   void ReadFill(Deserializer* d) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       OneByteStringPtr str = static_cast<OneByteStringPtr>(d->Ref(id));
-      const intptr_t length = d->ReadUnsigned();
-      bool is_canonical = d->Read<bool>();
+      const intptr_t combined = d->ReadUnsigned();
+      const intptr_t length = combined >> 1;
+      const bool is_canonical = (combined & 1) != 0;
       Deserializer::InitializeHeader(str, kOneByteStringCid,
                                      OneByteString::InstanceSize(length),
                                      is_canonical);
       str->ptr()->length_ = Smi::New(length);
-      String::SetCachedHash(str, d->Read<int32_t>());
+      uint32_t hash = 0;
       for (intptr_t j = 0; j < length; j++) {
-        str->ptr()->data()[j] = d->Read<uint8_t>();
+        uint8_t code_point = d->Read<uint8_t>();
+        str->ptr()->data()[j] = code_point;
+        hash = CombineHashes(hash, code_point);
       }
+      String::SetCachedHash(str, FinalizeHash(hash, String::kHashBits));
     }
   }
 };
@@ -4924,10 +4926,8 @@
       TwoByteStringPtr str = objects_[i];
       AutoTraceObject(str);
       const intptr_t length = Smi::Value(str->ptr()->length_);
-      s->WriteUnsigned(length);
-      s->Write<bool>(str->ptr()->IsCanonical());
-      intptr_t hash = String::GetCachedHash(str);
-      s->Write<int32_t>(hash);
+      ASSERT(length <= (compiler::target::kSmiMax / 2));
+      s->WriteUnsigned((length << 1) | (str->ptr()->IsCanonical() ? 1 : 0));
       s->WriteBytes(reinterpret_cast<uint8_t*>(str->ptr()->data()), length * 2);
     }
   }
@@ -4957,15 +4957,16 @@
   void ReadFill(Deserializer* d) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TwoByteStringPtr str = static_cast<TwoByteStringPtr>(d->Ref(id));
-      const intptr_t length = d->ReadUnsigned();
-      bool is_canonical = d->Read<bool>();
+      const intptr_t combined = d->ReadUnsigned();
+      const intptr_t length = combined >> 1;
+      const bool is_canonical = (combined & 1) != 0;
       Deserializer::InitializeHeader(str, kTwoByteStringCid,
                                      TwoByteString::InstanceSize(length),
                                      is_canonical);
       str->ptr()->length_ = Smi::New(length);
-      String::SetCachedHash(str, d->Read<int32_t>());
       uint8_t* cdata = reinterpret_cast<uint8_t*>(str->ptr()->data());
       d->ReadBytes(cdata, length * 2);
+      String::SetCachedHash(str, String::Hash(str));
     }
   }
 };
@@ -5622,10 +5623,6 @@
       return "CodeSourceMap";
     case kCompressedStackMapsCid:
       return "CompressedStackMaps";
-    case kOneByteStringCid:
-      return "OneByteString";
-    case kTwoByteStringCid:
-      return "TwoByteString";
     default:
       return nullptr;
   }
@@ -6352,8 +6349,6 @@
       case kPcDescriptorsCid:
       case kCodeSourceMapCid:
       case kCompressedStackMapsCid:
-      case kOneByteStringCid:
-      case kTwoByteStringCid:
         return new (Z) RODataDeserializationCluster();
     }
   }