Removed SkSL::StringFragment in favor of string_view

This CL preserves the "StringFragment" name as an alias for
string_view to reduce the impact. The StringFragment alias
will be removed in a followup CL.

Change-Id: I89209bc626b0be0d0190823b6217f4c83cafe1bc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/416736
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 329c859..5bbf761 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -624,6 +624,7 @@
       "src/core/SkStream.cpp",
       "src/core/SkString.cpp",
       "src/core/SkStringUtils.cpp",
+      "src/core/SkStringView.cpp",
       "src/core/SkThreadID.cpp",
       "src/core/SkUtils.cpp",
       "src/core/SkVM.cpp",
diff --git a/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp b/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp
index 3090039..602b62e 100644
--- a/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp
+++ b/fuzz/oss_fuzz/FuzzSKSL2Pipeline.cpp
@@ -30,7 +30,7 @@
         using String = SkSL::String;
 
         String declareUniform(const SkSL::VarDeclaration* decl) override {
-            return decl->var().name();
+            return String(decl->var().name());
         }
 
         void defineFunction(const char* /*decl*/, const char* /*body*/, bool /*isMain*/) override {}
diff --git a/include/core/SkString.h b/include/core/SkString.h
index e4bd7b7..77540f3 100644
--- a/include/core/SkString.h
+++ b/include/core/SkString.h
@@ -20,6 +20,10 @@
 #include <atomic>
 #include <string>
 
+namespace skstd {
+    class string_view;
+}
+
 /*  Some helper functions for C strings */
 static inline bool SkStrStartsWith(const char string[], const char prefixStr[]) {
     SkASSERT(string);
@@ -122,6 +126,7 @@
                 SkString(const SkString&);
                 SkString(SkString&&);
     explicit    SkString(const std::string&);
+    explicit    SkString(skstd::string_view);
                 ~SkString();
 
     bool        isEmpty() const { return 0 == fRec->fLength; }
diff --git a/include/core/SkStringView.h b/include/core/SkStringView.h
index 7912841..c705b11 100644
--- a/include/core/SkStringView.h
+++ b/include/core/SkStringView.h
@@ -109,6 +109,15 @@
         other.fLength = tempLength;
     }
 
+    constexpr void remove_prefix(size_type n) {
+        fData += n;
+        fLength -= n;
+    }
+
+    constexpr void remove_suffix(size_type n) {
+        fLength -= n;
+    }
+
 private:
     const_pointer fData;
     size_type fLength;
@@ -128,4 +137,16 @@
 
 } // namespace skstd
 
+namespace std {
+    template<> struct hash<skstd::string_view> {
+        size_t operator()(const skstd::string_view& s) const {
+            size_t result = 0;
+            for (auto iter = s.begin(); iter != s.end(); ++iter) {
+                result = result * 101 + (size_t) *iter;
+            }
+            return result;
+        }
+    };
+} // namespace std
+
 #endif
diff --git a/include/private/SkSLLayout.h b/include/private/SkSLLayout.h
index d7c764a..71859a5 100644
--- a/include/private/SkSLLayout.h
+++ b/include/private/SkSLLayout.h
@@ -208,7 +208,7 @@
         if (fInvocations >= 0) {
             result += separator() + "invocations = " + to_string(fInvocations);
         }
-        if (fWhen.fLength) {
+        if (fWhen.length()) {
             result += separator() + "when = " + fWhen;
         }
         if (result.size() > 0) {
diff --git a/include/private/SkSLString.h b/include/private/SkSLString.h
index b1f4a85f..74e1cea 100644
--- a/include/private/SkSLString.h
+++ b/include/private/SkSLString.h
@@ -22,62 +22,13 @@
 
 class String;
 
-// Represents a (not necessarily null-terminated) slice of a string.
-struct StringFragment {
-    StringFragment()
-        : fChars("")
-        , fLength(0) {}
-
-    StringFragment(const char* chars)
-        : fChars(chars)
-        , fLength(strlen(chars)) {}
-
-    StringFragment(const char* chars, size_t length)
-        : fChars(chars)
-        , fLength(length) {}
-
-    StringFragment(skstd::string_view s)
-        : fChars(s.data())
-        , fLength(s.length()) {}
-
-    const char* begin() const { return fChars; }
-    const char* end() const { return fChars + fLength; }
-
-    const char* data() const { return fChars; }
-    size_t size() const { return fLength; }
-    size_t length() const { return fLength; }
-    char operator[](size_t idx) const { return fChars[idx]; }
-
-    bool startsWith(const char prefix[]) const;
-    bool endsWith(const char suffix[]) const;
-
-    bool operator==(const char* s) const;
-    bool operator!=(const char* s) const;
-    bool operator==(StringFragment s) const;
-    bool operator!=(StringFragment s) const;
-    bool operator<(StringFragment s) const;
-    String operator+(const char* s) const;
-    String operator+(const StringFragment& s) const;
-    String operator+(const String& s) const;
-
-#ifndef SKSL_STANDALONE
-    operator SkString() const { return SkString(fChars, fLength); }
-#endif
-
-    const char* fChars;
-    size_t fLength;
-};
-
-bool operator==(const char* s1, StringFragment s2);
-
-bool operator!=(const char* s1, StringFragment s2);
+using StringFragment = skstd::string_view;
 
 class SK_API String : public std::string {
 public:
     using std::string::string;
 
     explicit String(std::string s) : INHERITED(std::move(s)) {}
-    String(StringFragment s) : INHERITED(s.fChars, s.fLength) {}
     explicit String(skstd::string_view s) : INHERITED(s.data(), s.length()) {}
     // TODO(johnstiles): add operator skstd::string_view
 
@@ -85,11 +36,11 @@
     void appendf(const char* fmt, ...) SK_PRINTF_LIKE(2, 3);
     void vappendf(const char* fmt, va_list va);
 
-    bool startsWith(const char prefix[]) const {
-        return StringFragment(data(), size()).startsWith(prefix);
+    bool starts_with(const char prefix[]) const {
+        return StringFragment(data(), size()).starts_with(prefix);
     }
-    bool endsWith(const char suffix[]) const {
-        return StringFragment(data(), size()).endsWith(suffix);
+    bool ends_with(const char suffix[]) const {
+        return StringFragment(data(), size()).ends_with(suffix);
     }
 
     bool consumeSuffix(const char suffix[]);
@@ -101,20 +52,13 @@
     String& operator+=(const char* s);
     String& operator+=(const String& s);
     String& operator+=(StringFragment s);
-    bool operator==(const char* s) const;
-    bool operator!=(const char* s) const;
-    bool operator==(const String& s) const;
-    bool operator!=(const String& s) const;
     friend String operator+(const char* s1, const String& s2);
-    friend bool operator==(const char* s1, const String& s2);
-    friend bool operator!=(const char* s1, const String& s2);
 
 private:
     using INHERITED = std::string;
 };
 
-String operator+(const char* s1, const String& s2);
-bool operator!=(const char* s1, const String& s2);
+String operator+(StringFragment left, StringFragment right);
 
 String to_string(double value);
 String to_string(int32_t value);
@@ -128,16 +72,6 @@
 } // namespace SkSL
 
 namespace std {
-    template<> struct hash<SkSL::StringFragment> {
-        size_t operator()(const SkSL::StringFragment& s) const {
-            size_t result = 0;
-            for (size_t i = 0; i < s.fLength; ++i) {
-                result = result * 101 + (size_t) s.fChars[i];
-            }
-            return result;
-        }
-    };
-
     template<> struct hash<SkSL::String> {
         size_t operator()(const SkSL::String& s) const {
             return hash<std::string>{}(s);
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index db02bfe..ec04c41 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -223,7 +223,7 @@
             // Child effects that can be sampled ('shader' or 'colorFilter')
             if (varType.isEffectChild()) {
                 Child c;
-                c.name  = var.name();
+                c.name  = SkString(var.name());
                 c.type  = child_type(varType);
                 c.index = children.size();
                 children.push_back(c);
@@ -233,7 +233,7 @@
             // 'uniform' variables
             else if (var.modifiers().fFlags & SkSL::Modifiers::kUniform_Flag) {
                 Uniform uni;
-                uni.name = var.name();
+                uni.name = SkString(var.name());
                 uni.flags = 0;
                 uni.count = 1;
 
diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp
index bf0078b..810d9c0 100644
--- a/src/core/SkString.cpp
+++ b/src/core/SkString.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "include/core/SkString.h"
+#include "include/core/SkStringView.h"
 #include "include/private/SkTPin.h"
 #include "include/private/SkTo.h"
 #include "src/core/SkSafeMath.h"
@@ -294,6 +295,10 @@
     fRec = Rec::Make(src.c_str(), src.size());
 }
 
+SkString::SkString(skstd::string_view src) {
+    fRec = Rec::Make(src.data(), src.length());
+}
+
 SkString::~SkString() {
     this->validate();
 }
diff --git a/src/sksl/SkSLASTNode.cpp b/src/sksl/SkSLASTNode.cpp
index b47e7e2..9f43d30 100644
--- a/src/sksl/SkSLASTNode.cpp
+++ b/src/sksl/SkSLASTNode.cpp
@@ -67,7 +67,7 @@
             if (this->begin() != this->end()) {
                 return String(getString()) + " = " + this->begin()->description();
             }
-            return getString();
+            return String(getString());
         case Kind::kExtension:
             return "#extension " + getString();
         case Kind::kField:
@@ -111,7 +111,7 @@
             return result;
         }
         case Kind::kIdentifier:
-            return getString();
+            return String(getString());
         case Kind::kIndex:
             return this->begin()->description() + "[" + (this->begin() + 1)->description() + "]";
         case Kind::kIf: {
@@ -204,10 +204,10 @@
             return "(" + this->begin()->description() + " ? " + (this->begin() + 1)->description() +
                    " : " + (this->begin() + 2)->description() + ")";
         case Kind::kType:
-            return getString();
+            return String(getString());
         case Kind::kVarDeclaration: {
             const VarData& vd = getVarData();
-            String result = vd.fName;
+            String result(vd.fName);
             auto iter = this->begin();
             if (vd.fIsArray) {
                 result += "[" + (iter++)->description() + "]";
diff --git a/src/sksl/SkSLASTNode.h b/src/sksl/SkSLASTNode.h
index c971868..8fc24b4 100644
--- a/src/sksl/SkSLASTNode.h
+++ b/src/sksl/SkSLASTNode.h
@@ -475,6 +475,7 @@
         return *reinterpret_cast<const SKSL_FLOAT*>(fData.fBytes);
     }
 
+    // TODO(ethannicholas): Rename this to getStringView() as part of changing the return type
     const StringFragment& getString() const {
         SkASSERT(fData.fKind == NodeData::Kind::kStringFragment);
         return *reinterpret_cast<const StringFragment*>(fData.fBytes);
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 5ad03e9..ecdcd10 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -409,20 +409,20 @@
             case ProgramElement::Kind::kEnum: {
                 const Enum& e = element->as<Enum>();
                 SkASSERT(e.isBuiltin());
-                intrinsics->insertOrDie(e.typeName(), std::move(element));
+                intrinsics->insertOrDie(String(e.typeName()), std::move(element));
                 break;
             }
             case ProgramElement::Kind::kGlobalVar: {
                 const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
                 const Variable& var = global.declaration()->as<VarDeclaration>().var();
                 SkASSERT(var.isBuiltin());
-                intrinsics->insertOrDie(var.name(), std::move(element));
+                intrinsics->insertOrDie(String(var.name()), std::move(element));
                 break;
             }
             case ProgramElement::Kind::kInterfaceBlock: {
                 const Variable& var = element->as<InterfaceBlock>().variable();
                 SkASSERT(var.isBuiltin());
-                intrinsics->insertOrDie(var.name(), std::move(element));
+                intrinsics->insertOrDie(String(var.name()), std::move(element));
                 break;
             }
             default:
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index db3c477..7648a0c 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -270,7 +270,7 @@
                     offset,
                     "'in uniform' variables only permitted within fragment processors");
         }
-        if (modifiers.fLayout.fWhen.fLength) {
+        if (modifiers.fLayout.fWhen.length()) {
             this->errorReporter().error(offset,
                                         "'when' is only permitted within fragment processors");
         }
@@ -1061,7 +1061,8 @@
             }
         }
     }
-    const Type* type = old->takeOwnershipOfSymbol(Type::MakeStructType(intf.fOffset, id.fTypeName,
+    const Type* type = old->takeOwnershipOfSymbol(Type::MakeStructType(intf.fOffset,
+                                                                       String(id.fTypeName),
                                                                        fields));
     int arraySize = 0;
     if (id.fIsArray) {
@@ -1082,14 +1083,14 @@
     const Variable* var = old->takeOwnershipOfSymbol(
             std::make_unique<Variable>(intf.fOffset,
                                        this->modifiersPool().add(id.fModifiers),
-                                       id.fInstanceName.fLength ? id.fInstanceName : id.fTypeName,
+                                       id.fInstanceName.length() ? id.fInstanceName : id.fTypeName,
                                        type,
                                        fIsBuiltinCode,
                                        Variable::Storage::kGlobal));
     if (foundRTAdjust) {
         fRTAdjustInterfaceBlock = var;
     }
-    if (id.fInstanceName.fLength) {
+    if (id.fInstanceName.length()) {
         old->addWithoutOwnership(var);
     } else {
         for (size_t i = 0; i < fields.size(); i++) {
@@ -1098,8 +1099,8 @@
     }
     return std::make_unique<InterfaceBlock>(intf.fOffset,
                                             var,
-                                            id.fTypeName,
-                                            id.fInstanceName,
+                                            String(id.fTypeName),
+                                            String(id.fInstanceName),
                                             arraySize,
                                             symbols);
 }
@@ -1334,8 +1335,7 @@
     }
 
     const ASTNode::SectionData& section = s.getSectionData();
-    return std::make_unique<Section>(s.fOffset, section.fName, section.fArgument,
-                                                section.fText);
+    return std::make_unique<Section>(s.fOffset, section.fName, section.fArgument, section.fText);
 }
 
 std::unique_ptr<Expression> IRGenerator::coerce(std::unique_ptr<Expression> expr,
@@ -1526,7 +1526,7 @@
 // secondary swizzle to put them back into the right order, so in this case we end up with
 // 'float4(base.xw, 1, 0).xzyw'.
 std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expression> base,
-                                                        String fields) {
+                                                        StringFragment fields) {
     const int offset = base->fOffset;
     const Type& baseType = base->type();
     if (!baseType.isVector() && !baseType.isNumber()) {
@@ -1623,7 +1623,7 @@
     }
     // ... and if that fails, check the intrinsics, add it to our shared elements
     if (!enumElement && !fIsBuiltinCode && fIntrinsics) {
-        if (const ProgramElement* found = fIntrinsics->findAndInclude(type.name())) {
+        if (const ProgramElement* found = fIntrinsics->findAndInclude(String(type.name()))) {
             fSharedElements->push_back(found);
             enumElement = found;
         }
@@ -1792,7 +1792,7 @@
 
         bool visitExpression(const Expression& e) override {
             if (e.is<VariableReference>() && e.as<VariableReference>().variable()->isBuiltin()) {
-                this->addDeclaringElement(e.as<VariableReference>().variable()->name());
+                this->addDeclaringElement(String(e.as<VariableReference>().variable()->name()));
             }
             return INHERITED::visitExpression(e);
         }
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index 602b08c..b446a28 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -222,7 +222,8 @@
     std::unique_ptr<StructDefinition> convertStructDefinition(const ASTNode& expression);
     std::unique_ptr<Expression> convertTypeField(int offset, const Type& type,
                                                  StringFragment field);
-    std::unique_ptr<Expression> convertSwizzle(std::unique_ptr<Expression> base, String fields);
+    std::unique_ptr<Expression> convertSwizzle(std::unique_ptr<Expression> base,
+                                               StringFragment fields);
     std::unique_ptr<Expression> convertTernaryExpression(const ASTNode& expression);
     std::unique_ptr<Statement> convertVarDeclarationStatement(const ASTNode& s);
     std::unique_ptr<Statement> convertWhile(const ASTNode& w);
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index 7356f97..c285397 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -541,7 +541,7 @@
             // regard, but see `InlinerAvoidsVariableNameOverlap` for a counterexample where unique
             // names are important.
             const String* name = symbolTableForStatement->takeOwnershipOfString(
-                    fMangler.uniqueName(variable.name(), symbolTableForStatement));
+                    fMangler.uniqueName(String(variable.name()), symbolTableForStatement));
             auto clonedVar = std::make_unique<Variable>(
                                                      offset,
                                                      &variable.modifiers(),
@@ -640,7 +640,7 @@
         // for void-return functions, or in cases that are simple enough that we can just replace
         // the function-call node with the result expression.
         std::unique_ptr<Expression> noInitialValue;
-        InlineVariable var = this->makeInlineVariable(function.declaration().name(),
+        InlineVariable var = this->makeInlineVariable(String(function.declaration().name()),
                                                       &function.declaration().returnType(),
                                                       symbolTable.get(), Modifiers{},
                                                       caller->isBuiltin(), &noInitialValue);
@@ -665,7 +665,7 @@
                 continue;
             }
         }
-        InlineVariable var = this->makeInlineVariable(param->name(), &arguments[i]->type(),
+        InlineVariable var = this->makeInlineVariable(String(param->name()), &arguments[i]->type(),
                                                       symbolTable.get(), param->modifiers(),
                                                       caller->isBuiltin(), &arguments[i]);
         inlineStatements.push_back(std::move(var.fVarDecl));
diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp
index 516788b..720fe49 100644
--- a/src/sksl/SkSLMain.cpp
+++ b/src/sksl/SkSLMain.cpp
@@ -283,19 +283,19 @@
 
     SkSL::ProgramKind kind;
     const SkSL::String& inputPath = args[1];
-    if (inputPath.endsWith(".vert")) {
+    if (inputPath.ends_with(".vert")) {
         kind = SkSL::ProgramKind::kVertex;
-    } else if (inputPath.endsWith(".frag") || inputPath.endsWith(".sksl")) {
+    } else if (inputPath.ends_with(".frag") || inputPath.ends_with(".sksl")) {
         kind = SkSL::ProgramKind::kFragment;
-    } else if (inputPath.endsWith(".geom")) {
+    } else if (inputPath.ends_with(".geom")) {
         kind = SkSL::ProgramKind::kGeometry;
-    } else if (inputPath.endsWith(".fp")) {
+    } else if (inputPath.ends_with(".fp")) {
         kind = SkSL::ProgramKind::kFragmentProcessor;
-    } else if (inputPath.endsWith(".rtb")) {
+    } else if (inputPath.ends_with(".rtb")) {
         kind = SkSL::ProgramKind::kRuntimeBlend;
-    } else if (inputPath.endsWith(".rtcf")) {
+    } else if (inputPath.ends_with(".rtcf")) {
         kind = SkSL::ProgramKind::kRuntimeColorFilter;
-    } else if (inputPath.endsWith(".rts")) {
+    } else if (inputPath.ends_with(".rts")) {
         kind = SkSL::ProgramKind::kRuntimeShader;
     } else {
         printf("input filename must end in '.vert', '.frag', '.geom', '.fp', '.rtb', '.rtcf', "
@@ -350,13 +350,13 @@
         return ResultCode::kSuccess;
     };
 
-    if (outputPath.endsWith(".spirv")) {
+    if (outputPath.ends_with(".spirv")) {
         return compileProgram(
                 [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
                     return compiler.toSPIRV(program, out);
                 });
-    } else if (outputPath.endsWith(".asm.frag") || outputPath.endsWith(".asm.vert") ||
-               outputPath.endsWith(".asm.geom")) {
+    } else if (outputPath.ends_with(".asm.frag") || outputPath.ends_with(".asm.vert") ||
+               outputPath.ends_with(".asm.geom")) {
         return compileProgram(
                 [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
                     // Compile program to SPIR-V assembly in a string-stream.
@@ -376,24 +376,24 @@
                     out.write(disassembly.data(), disassembly.size());
                     return true;
                 });
-    } else if (outputPath.endsWith(".glsl")) {
+    } else if (outputPath.ends_with(".glsl")) {
         return compileProgram(
                 [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
                     return compiler.toGLSL(program, out);
                 });
-    } else if (outputPath.endsWith(".metal")) {
+    } else if (outputPath.ends_with(".metal")) {
         return compileProgram(
                 [](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
                     return compiler.toMetal(program, out);
                 });
-    } else if (outputPath.endsWith(".h")) {
+    } else if (outputPath.ends_with(".h")) {
         settings.fReplaceSettings = false;
         settings.fPermitInvalidStaticTests = true;
         return compileProgram(
                 [&](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
                     return compiler.toH(program, base_name(inputPath.c_str(), "Gr", ".fp"), out);
                 });
-    } else if (outputPath.endsWith(".dsl.cpp")) {
+    } else if (outputPath.ends_with(".dsl.cpp")) {
         settings.fReplaceSettings = false;
         settings.fPermitInvalidStaticTests = true;
         return compileProgram(
@@ -401,14 +401,14 @@
                     return compiler.toDSLCPP(program, base_name(inputPath.c_str(), "Gr", ".fp"),
                                              out);
                 });
-    } else if (outputPath.endsWith(".cpp")) {
+    } else if (outputPath.ends_with(".cpp")) {
         settings.fReplaceSettings = false;
         settings.fPermitInvalidStaticTests = true;
         return compileProgram(
                 [&](SkSL::Compiler& compiler, SkSL::Program& program, SkSL::OutputStream& out) {
                     return compiler.toCPP(program, base_name(inputPath.c_str(), "Gr", ".fp"), out);
                 });
-    } else if (outputPath.endsWith(".skvm")) {
+    } else if (outputPath.ends_with(".skvm")) {
         return compileProgram(
                 [](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) {
                     skvm::Builder builder{skvm::Features{}};
@@ -420,7 +420,7 @@
                     builder.done().dump(redirect.get());
                     return true;
                 });
-    } else if (outputPath.endsWith(".stage")) {
+    } else if (outputPath.ends_with(".stage")) {
         return compileProgram(
                 [](SkSL::Compiler&, SkSL::Program& program, SkSL::OutputStream& out) {
                     class Callbacks : public SkSL::PipelineStage::Callbacks {
@@ -433,7 +433,7 @@
 
                         String declareUniform(const SkSL::VarDeclaration* decl) override {
                             fOutput += decl->description();
-                            return decl->var().name();
+                            return String(decl->var().name());
                         }
 
                         void defineFunction(const char* decl,
@@ -481,7 +481,7 @@
                     out.writeString(GrShaderUtils::PrettyPrint(callbacks.fOutput));
                     return true;
                 });
-    } else if (outputPath.endsWith(".dehydrated.sksl")) {
+    } else if (outputPath.ends_with(".dehydrated.sksl")) {
         SkSL::FileOutputStream out(outputPath);
         SkSL::Compiler compiler(caps);
         if (!out.isValid()) {
@@ -522,7 +522,7 @@
  */
 ResultCode processWorklist(const char* worklistPath) {
     SkSL::String inputPath(worklistPath);
-    if (!inputPath.endsWith(".worklist")) {
+    if (!inputPath.ends_with(".worklist")) {
         printf("expected .worklist file, found: %s\n\n", worklistPath);
         show_usage();
         return ResultCode::kConfigurationError;
diff --git a/src/sksl/SkSLMangler.cpp b/src/sksl/SkSLMangler.cpp
index 9b6c7fa..2ccd72c 100644
--- a/src/sksl/SkSLMangler.cpp
+++ b/src/sksl/SkSLMangler.cpp
@@ -14,7 +14,7 @@
     SkASSERT(symbolTable);
     // The inliner runs more than once, so the base name might already have been mangled and have a
     // prefix like "_123_x". Let's strip that prefix off to make the generated code easier to read.
-    if (baseName.startsWith("_")) {
+    if (baseName.starts_with("_")) {
         // Determine if we have a string of digits.
         int offset = 1;
         while (isdigit(baseName[offset])) {
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index 2e93694..55e44ad 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -335,14 +335,14 @@
     if (!this->expect(Token::Kind::TK_LBRACE, "'{'")) {
         return ASTNode::ID::Invalid();
     }
-    StringFragment text;
     Token codeStart = this->nextRawToken();
     size_t startOffset = codeStart.fOffset;
     this->pushback(codeStart);
-    text.fChars = fText.begin() + startOffset;
     int level = 1;
-    for (;;) {
-        Token next = this->nextRawToken();
+    StringFragment text;
+    Token next;
+    while (level > 0) {
+        next = this->nextRawToken();
         switch (next.fKind) {
             case Token::Kind::TK_LBRACE:
                 ++level;
@@ -356,14 +356,10 @@
             default:
                 break;
         }
-        if (!level) {
-            text.fLength = next.fOffset - startOffset;
-            break;
-        }
     }
+    text = StringFragment(fText.begin() + startOffset, next.fOffset - startOffset);
     StringFragment name = this->text(start);
-    ++name.fChars;
-    --name.fLength;
+    name.remove_prefix(1);
     return this->createNode(start.fOffset, ASTNode::Kind::kSection,
                             ASTNode::SectionData(name, argument, text));
 }
@@ -385,7 +381,7 @@
     if (!this->expect(Token::Kind::TK_LBRACE, "'{'")) {
         return ASTNode::ID::Invalid();
     }
-    fSymbols.add(Type::MakeEnumType(this->text(name)));
+    fSymbols.add(Type::MakeEnumType(String(this->text(name))));
     ASTNode::ID result = this->createNode(name.fOffset, ASTNode::Kind::kEnum, this->text(name));
     if (!this->checkNext(Token::Kind::TK_RBRACE)) {
         Token id;
@@ -629,7 +625,8 @@
                     "struct '" + this->text(name) + "' must contain at least one field");
         return ASTNode::ID::Invalid();
     }
-    std::unique_ptr<Type> newType = Type::MakeStructType(name.fOffset, this->text(name), fields);
+    std::unique_ptr<Type> newType = Type::MakeStructType(name.fOffset, String(this->text(name)),
+                                                         fields);
     if (struct_is_too_deeply_nested(*newType, kMaxStructDepth)) {
         this->error(name.fOffset, "struct '" + this->text(name) + "' is too deeply nested");
         return ASTNode::ID::Invalid();
@@ -809,7 +806,6 @@
     Token start = this->nextRawToken();
     this->pushback(start);
     StringFragment code;
-    code.fChars = fText.begin() + start.fOffset;
     int level = 1;
     bool done = false;
     while (!done) {
@@ -836,7 +832,7 @@
             done = true;
         }
         if (done) {
-            code.fLength = next.fOffset - start.fOffset;
+            code = StringFragment(fText.begin() + start.fOffset, next.fOffset - start.fOffset);
             this->pushback(std::move(next));
         }
     }
@@ -846,7 +842,7 @@
 Layout::CType Parser::layoutCType() {
     if (this->expect(Token::Kind::TK_EQ, "'='")) {
         Token t = this->nextToken();
-        String text = this->text(t);
+        String text(this->text(t));
         auto found = layoutTokens->find(text);
         if (found != layoutTokens->end()) {
             switch (found->second) {
@@ -885,7 +881,7 @@
         }
         for (;;) {
             Token t = this->nextToken();
-            String text = this->text(t);
+            String text(this->text(t));
             auto setFlag = [&](Layout::Flag f) {
                 if (flags & f) {
                     this->error(t, "layout qualifier '" + text + "' appears more than once");
@@ -2021,11 +2017,10 @@
             // Swizzles that start with a constant number, e.g. '.000r', will be tokenized as
             // floating point literals, possibly followed by an identifier. Handle that here.
             StringFragment field = this->text(next);
-            SkASSERT(field.fChars[0] == '.');
-            ++field.fChars;
-            --field.fLength;
-            for (size_t i = 0; i < field.fLength; ++i) {
-                if (field.fChars[i] != '0' && field.fChars[i] != '1') {
+            SkASSERT(field[0] == '.');
+            field.remove_prefix(1);
+            for (auto iter = field.begin(); iter != field.end(); ++iter) {
+                if (*iter != '0' && *iter != '1') {
                     this->error(next, "invalid swizzle");
                     return ASTNode::ID::Invalid();
                 }
@@ -2034,7 +2029,7 @@
             // identifiers that directly follow the float
             Token id = this->nextRawToken();
             if (id.fKind == Token::Kind::TK_IDENTIFIER) {
-                field.fLength += id.fLength;
+                field = StringFragment(field.data(), field.length() + id.fLength);
             } else {
                 this->pushback(id);
             }
diff --git a/src/sksl/SkSLRehydrator.cpp b/src/sksl/SkSLRehydrator.cpp
index 378120a..1d18550 100644
--- a/src/sksl/SkSLRehydrator.cpp
+++ b/src/sksl/SkSLRehydrator.cpp
@@ -150,7 +150,7 @@
             uint16_t id = this->readU16();
             const Type* componentType = this->type();
             int8_t count = this->readS8();
-            String name = componentType->name();
+            String name(componentType->name());
             if (count == Type::kUnsizedArray) {
                 name += "[]";
             } else {
@@ -164,7 +164,8 @@
         case kEnumType_Command: {
             uint16_t id = this->readU16();
             StringFragment name = this->readString();
-            const Type* result = fSymbolTable->takeOwnershipOfSymbol(Type::MakeEnumType(name));
+            const Type* result =
+                    fSymbolTable->takeOwnershipOfSymbol(Type::MakeEnumType(String(name)));
             this->addSymbol(id, result);
             return result;
         }
@@ -199,7 +200,7 @@
         }
         case kStructType_Command: {
             uint16_t id = this->readU16();
-            StringFragment name = this->readString();
+            String name(this->readString());
             uint8_t fieldCount = this->readU8();
             std::vector<Type::Field> fields;
             fields.reserve(fieldCount);
@@ -324,11 +325,12 @@
         case Rehydrator::kInterfaceBlock_Command: {
             const Symbol* var = this->symbol();
             SkASSERT(var && var->is<Variable>());
-            StringFragment typeName = this->readString();
-            StringFragment instanceName = this->readString();
+            String typeName(this->readString());
+            String instanceName(this->readString());
             int arraySize = this->readS8();
-            return std::make_unique<InterfaceBlock>(/*offset=*/-1, &var->as<Variable>(), typeName,
-                                                    instanceName, arraySize, nullptr);
+            return std::make_unique<InterfaceBlock>(/*offset=*/-1, &var->as<Variable>(),
+                                                    std::move(typeName), std::move(instanceName),
+                                                    arraySize, nullptr);
         }
         case Rehydrator::kVarDeclarations_Command: {
             std::unique_ptr<Statement> decl = this->statement();
@@ -543,7 +545,7 @@
             return PrefixExpression::Make(fContext, op, std::move(operand));
         }
         case Rehydrator::kSetting_Command: {
-            StringFragment name = this->readString();
+            String name(this->readString());
             return Setting::Convert(fContext, /*offset=*/-1, name);
         }
         case Rehydrator::kSwizzle_Command: {
diff --git a/src/sksl/SkSLSectionAndParameterHelper.cpp b/src/sksl/SkSLSectionAndParameterHelper.cpp
index 640a618..915c052 100644
--- a/src/sksl/SkSLSectionAndParameterHelper.cpp
+++ b/src/sksl/SkSLSectionAndParameterHelper.cpp
@@ -38,15 +38,15 @@
             }
             case ProgramElement::Kind::kSection: {
                 const Section& s = p->as<Section>();
-                const String& name = s.name();
-                const String& arg = s.argument();
-                if (IsSupportedSection(name.c_str())) {
-                    if (SectionRequiresArgument(name.c_str()) && !arg.size()) {
+                StringFragment name = s.name();
+                StringFragment arg = s.argument();
+                if (IsSupportedSection(name)) {
+                    if (SectionRequiresArgument(name) && !arg.size()) {
                         errors.error(s.fOffset,
                                      ("section '@" + name +
                                       "' requires one parameter").c_str());
                     }
-                    if (!SectionAcceptsArgument(name.c_str()) && arg.size()) {
+                    if (!SectionAcceptsArgument(name) && arg.size()) {
                         errors.error(s.fOffset,
                                      ("section '@" + name + "' has no parameters").c_str());
                     }
diff --git a/src/sksl/SkSLSectionAndParameterHelper.h b/src/sksl/SkSLSectionAndParameterHelper.h
index 9ca7b83..f3fc376 100644
--- a/src/sksl/SkSLSectionAndParameterHelper.h
+++ b/src/sksl/SkSLSectionAndParameterHelper.h
@@ -39,7 +39,7 @@
 public:
     SectionAndParameterHelper(const Program* program, ErrorReporter& errors);
 
-    const Section* getSection(const char* name) {
+    const Section* getSection(StringFragment name) {
         auto found = fSections.find(name);
         if (found == fSections.end()) {
             return nullptr;
@@ -48,7 +48,7 @@
         return found->second[0];
     }
 
-    std::vector<const Section*> getSections(const char* name) {
+    std::vector<const Section*> getSections(StringFragment name) {
         auto found = fSections.find(name);
         if (found == fSections.end()) {
             return std::vector<const Section*>();
@@ -65,40 +65,38 @@
                -1 == var.modifiers().fLayout.fBuiltin;
     }
 
-    static bool IsSupportedSection(const char* name) {
-        return !strcmp(name, kClassSection) ||
-               !strcmp(name, kCloneSection) ||
-               !strcmp(name, kConstructorSection) ||
-               !strcmp(name, kConstructorCodeSection) ||
-               !strcmp(name, kConstructorParamsSection) ||
-               !strcmp(name, kCppSection) ||
-               !strcmp(name, kCppEndSection) ||
-               !strcmp(name, kDumpInfoSection) ||
-               !strcmp(name, kEmitCodeSection) ||
-               !strcmp(name, kFieldsSection) ||
-               !strcmp(name, kHeaderSection) ||
-               !strcmp(name, kHeaderEndSection) ||
-               !strcmp(name, kInitializersSection) ||
-               !strcmp(name, kMakeSection) ||
-               !strcmp(name, kOptimizationFlagsSection) ||
-               !strcmp(name, kSetDataSection) ||
-               !strcmp(name, kTestCodeSection);
+    static bool IsSupportedSection(StringFragment name) {
+        return name == kClassSection ||
+               name == kCloneSection ||
+               name == kConstructorSection ||
+               name == kConstructorCodeSection ||
+               name == kConstructorParamsSection ||
+               name == kCppSection ||
+               name == kCppEndSection ||
+               name == kDumpInfoSection ||
+               name == kEmitCodeSection ||
+               name == kFieldsSection ||
+               name == kHeaderSection ||
+               name == kHeaderEndSection ||
+               name == kInitializersSection ||
+               name == kMakeSection ||
+               name == kOptimizationFlagsSection ||
+               name == kSetDataSection ||
+               name == kTestCodeSection;
     }
 
-    static bool SectionAcceptsArgument(const char* name) {
-        return !strcmp(name, kSetDataSection) ||
-               !strcmp(name, kTestCodeSection);
+    static bool SectionAcceptsArgument(StringFragment name) {
+        return name == kSetDataSection || name == kTestCodeSection;
     }
 
-    static bool SectionRequiresArgument(const char* name) {
-        return !strcmp(name, kSetDataSection) ||
-               !strcmp(name, kTestCodeSection);
+    static bool SectionRequiresArgument(StringFragment name) {
+        return name == kSetDataSection || name == kTestCodeSection;
     }
 
 private:
     const Program& fProgram;
     std::vector<const Variable*> fParameters;
-    std::unordered_map<String, std::vector<const Section*>> fSections;
+    std::unordered_map<StringFragment, std::vector<const Section*>> fSections;
 };
 
 } // namespace SkSL
diff --git a/src/sksl/SkSLString.cpp b/src/sksl/SkSLString.cpp
index 2c6472c..b758294 100644
--- a/src/sksl/SkSLString.cpp
+++ b/src/sksl/SkSLString.cpp
@@ -49,18 +49,6 @@
     va_end(reuse);
 }
 
-bool StringFragment::startsWith(const char prefix[]) const {
-    return !strncmp(fChars, prefix, strlen(prefix));
-}
-
-bool StringFragment::endsWith(const char suffix[]) const {
-    size_t suffixLength = strlen(suffix);
-    if (fLength < suffixLength) {
-        return false;
-    }
-    return !strncmp(fChars + fLength - suffixLength, suffix, suffixLength);
-}
-
 bool String::consumeSuffix(const char suffix[]) {
     size_t suffixLength = strlen(suffix);
     if (this->length() < suffixLength) {
@@ -87,7 +75,7 @@
 
 String String::operator+(StringFragment s) const {
     String result(*this);
-    result.append(s.fChars, s.fLength);
+    result.append(s.data(), s.length());
     return result;
 }
 
@@ -107,98 +95,18 @@
 }
 
 String& String::operator+=(StringFragment s) {
-    this->append(s.fChars, s.fLength);
+    this->append(s.data(), s.length());
     return *this;
 }
 
-bool String::operator==(const String& s) const {
-    return this->size() == s.size() && !memcmp(c_str(), s.c_str(), this->size());
-}
-
-bool String::operator!=(const String& s) const {
-    return !(*this == s);
-}
-
-bool String::operator==(const char* s) const {
-    return this->size() == strlen(s) && !memcmp(c_str(), s, this->size());
-}
-
-bool String::operator!=(const char* s) const {
-    return !(*this == s);
-}
-
 String operator+(const char* s1, const String& s2) {
     String result(s1);
     result.append(s2);
     return result;
 }
 
-bool operator==(const char* s1, const String& s2) {
-    return s2 == s1;
-}
-
-bool operator!=(const char* s1, const String& s2) {
-    return s2 != s1;
-}
-
-bool StringFragment::operator==(StringFragment s) const {
-    if (fLength != s.fLength) {
-        return false;
-    }
-    return !memcmp(fChars, s.fChars, fLength);
-}
-
-bool StringFragment::operator!=(StringFragment s) const {
-    if (fLength != s.fLength) {
-        return true;
-    }
-    return memcmp(fChars, s.fChars, fLength);
-}
-
-bool StringFragment::operator==(const char* s) const {
-    for (size_t i = 0; i < fLength; ++i) {
-        if (fChars[i] != s[i]) {
-            return false;
-        }
-    }
-    return 0 == s[fLength];
-}
-
-bool StringFragment::operator!=(const char* s) const {
-    for (size_t i = 0; i < fLength; ++i) {
-        if (fChars[i] != s[i]) {
-            return true;
-        }
-    }
-    return 0 != s[fLength];
-}
-
-bool StringFragment::operator<(StringFragment other) const {
-    int comparison = strncmp(fChars, other.fChars, std::min(fLength, other.fLength));
-    if (comparison) {
-        return comparison < 0;
-    }
-    return fLength < other.fLength;
-}
-
-String StringFragment::operator+(const char* other) const {
-    return String(*this) + other;
-}
-
-String StringFragment::operator+(const StringFragment& other) const {
-    return String(*this) + other;
-}
-
-String StringFragment::operator+(const String& other) const {
-    return String(*this) + other;
-}
-
-bool operator==(const char* s1, StringFragment s2) {
-    return s2 == s1;
-}
-
-bool operator!=(const char* s1, StringFragment s2) {
-    return s2 != s1;
+String operator+(StringFragment left, StringFragment right) {
+    return String(left) + right;
 }
 
 String to_string(int32_t value) {
diff --git a/src/sksl/codegen/SkSLCPPCodeGenerator.cpp b/src/sksl/codegen/SkSLCPPCodeGenerator.cpp
index 77f1af8..6a2087a 100644
--- a/src/sksl/codegen/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLCPPCodeGenerator.cpp
@@ -63,7 +63,7 @@
 }
 
 String CPPCodeGenerator::getTypeName(const Type& type) {
-    return type.name();
+    return String(type.name());
 }
 
 void CPPCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
@@ -227,7 +227,7 @@
 
 void CPPCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
     if (is_private(var)) {
-        this->writeRuntimeValue(var.type(), var.modifiers().fLayout, var.name());
+        this->writeRuntimeValue(var.type(), var.modifiers().fLayout, String(var.name()));
     } else {
         this->writeExpression(value, Precedence::kTopLevel);
     }
@@ -268,11 +268,11 @@
             const Variable& var = *ref.variable();
             if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
                 this->write("%s");
-                String name = var.name();
+                String name(var.name());
                 String varCode = String::printf("args.fUniformHandler->getUniformCStr(%sVar)",
                                                 HCodeGenerator::FieldName(name.c_str()).c_str());
                 String code;
-                if (var.modifiers().fLayout.fWhen.fLength) {
+                if (var.modifiers().fLayout.fWhen.length()) {
                     code = String::printf("%sVar.isValid() ? %s : \"%s\"",
                                           HCodeGenerator::FieldName(name.c_str()).c_str(),
                                           varCode.c_str(),
@@ -456,7 +456,7 @@
         return;
     }
 
-    String funcName = decl.name();
+    String funcName(decl.name());
     this->addExtraEmitCodeLine(
             String::printf("SkString %s_name = fragBuilder->getMangledFunctionName(\"%s\");",
                            funcName.c_str(),
@@ -465,7 +465,7 @@
     String args = String::printf("const GrShaderVar %s_args[] = { ", funcName.c_str());
     const char* separator = "";
     for (const Variable* param : decl.parameters()) {
-        String paramName = param->name();
+        String paramName(param->name());
         args.appendf("%sGrShaderVar(\"%s\", %s)", separator, paramName.c_str(),
                                                   glsltype_string(fContext, param->type()));
         separator = ", ";
@@ -476,7 +476,7 @@
 }
 
 void CPPCodeGenerator::prototypeHelperFunction(const FunctionDeclaration& decl) {
-    String funcName = decl.name();
+    String funcName(decl.name());
     this->addExtraEmitCodeLine(String::printf(
             "fragBuilder->emitFunctionPrototype(%s, %s_name.c_str(), {%s_args, %zu});",
             glsltype_string(fContext, decl.returnType()),
@@ -512,7 +512,7 @@
         }
 
         fOut = oldOut;
-        String funcName = decl.name();
+        String funcName(decl.name());
 
         String funcImpl;
         if (!fFormatArgs.empty()) {
@@ -576,7 +576,7 @@
     if (!needs_uniform_var(var)) {
         return;
     }
-    if (var.modifiers().fLayout.fWhen.fLength) {
+    if (var.modifiers().fLayout.fWhen.length()) {
         this->writef("        if (%s) {\n    ", String(var.modifiers().fLayout.fWhen).c_str());
     }
     String name(var.name());
@@ -594,7 +594,7 @@
                      name.c_str(),
                      var.type().columns());
     }
-    if (var.modifiers().fLayout.fWhen.fLength) {
+    if (var.modifiers().fLayout.fWhen.length()) {
         this->write("        }\n");
     }
 }
@@ -946,7 +946,8 @@
             }
 
             this->writef("%s%s;\n",
-                         indent.c_str(), mapper->setUniform(pdman, uniformName, valueVar).c_str());
+                         indent.c_str(),
+                         mapper->setUniform(String(pdman), uniformName, valueVar).c_str());
 
             if (conditionalUniform) {
                 // Close the earlier precheck block
@@ -1044,7 +1045,7 @@
             String fieldName = HCodeGenerator::FieldName(String(param->name()).c_str());
             String runtimeValue = this->formatRuntimeValue(param->type(),
                                                            param->modifiers().fLayout,
-                                                           param->name(),
+                                                           String(param->name()),
                                                            &argumentList);
             formatString.appendf("%s%s=%s",
                                  formatString.empty() ? "" : ", ",
@@ -1151,7 +1152,7 @@
                     }
                     this->write(";\n");
                 }
-                if (var.modifiers().fLayout.fWhen.fLength) {
+                if (var.modifiers().fLayout.fWhen.length()) {
                     this->writef("if (%s) {", String(var.modifiers().fLayout.fWhen).c_str());
                 }
                 if (varType == *fContext.fTypes.fHalf4) {
@@ -1184,7 +1185,7 @@
                     SK_ABORT("NOT YET IMPLEMENTED: automatic key handling for %s\n",
                              varType.displayName().c_str());
                 }
-                if (var.modifiers().fLayout.fWhen.fLength) {
+                if (var.modifiers().fLayout.fWhen.length()) {
                     this->write("}");
                 }
             }
diff --git a/src/sksl/codegen/SkSLDSLCPPCodeGenerator.cpp b/src/sksl/codegen/SkSLDSLCPPCodeGenerator.cpp
index f5c1604..50cd9099 100644
--- a/src/sksl/codegen/SkSLDSLCPPCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLDSLCPPCodeGenerator.cpp
@@ -546,7 +546,7 @@
     // the variable (which we do need, to fill in the Var's initial value).
     std::vector<String> argumentList;
     (void) this->formatRuntimeValue(var.type(), var.modifiers().fLayout,
-                                    var.name(), &argumentList);
+                                    String(var.name()), &argumentList);
 
     this->write(this->getTypeName(var.type()));
     this->write("(");
@@ -690,7 +690,7 @@
 
 String DSLCPPCodeGenerator::getTypeName(const Type& type) {
     if (fCPPMode) {
-        return type.name();
+        return String(type.name());
     }
     switch (type.typeKind()) {
         case Type::TypeKind::kScalar:
@@ -711,7 +711,7 @@
 
         default:
             SK_ABORT("not yet supported: getTypeName of %s", type.displayName().c_str());
-            return type.name();
+            return String(type.name());
     }
 }
 
@@ -741,7 +741,7 @@
         }
         default:
             SK_ABORT("not yet supported: getDSLType of %s", type.displayName().c_str());
-            return type.name();
+            return String(type.name());
     }
 }
 
@@ -889,7 +889,7 @@
     }
 
     const char* varCppName = this->getVariableCppName(var);
-    if (var.modifiers().fLayout.fWhen.fLength) {
+    if (var.modifiers().fLayout.fWhen.length()) {
         // In cases where the `when` clause is true, we set up the Var normally.
         this->writef(
                 "Var %s;\n"
@@ -906,7 +906,7 @@
     this->writef("%.*sVar = VarUniformHandle(%s);\n",
                  (int)var.name().size(), var.name().data(), this->getVariableCppName(var));
 
-    if (var.modifiers().fLayout.fWhen.fLength) {
+    if (var.modifiers().fLayout.fWhen.length()) {
         this->writef("    DeclareGlobal(%s);\n", varCppName);
         // In cases where the `when` is false, we declare the Var as a const with a default value.
         this->writef("} else {\n"
@@ -1068,7 +1068,8 @@
             }
 
             this->writef("%s%s;\n",
-                         indent.c_str(), mapper->setUniform(pdman, uniformName, valueVar).c_str());
+                         indent.c_str(),
+                         mapper->setUniform(String(pdman), uniformName, valueVar).c_str());
 
             if (conditionalUniform) {
                 // Close the earlier precheck block
@@ -1165,7 +1166,7 @@
             String fieldName = HCodeGenerator::FieldName(String(param->name()).c_str());
             String runtimeValue = this->formatRuntimeValue(param->type(),
                                                            param->modifiers().fLayout,
-                                                           param->name(),
+                                                           String(param->name()),
                                                            &argumentList);
             formatString.appendf("%s%s=%s",
                                  formatString.empty() ? "" : ", ",
@@ -1271,7 +1272,7 @@
                     }
                     this->write(";\n");
                 }
-                if (var.modifiers().fLayout.fWhen.fLength) {
+                if (var.modifiers().fLayout.fWhen.length()) {
                     this->writef("if (%s) {\n", String(var.modifiers().fLayout.fWhen).c_str());
                 }
                 if (varType == *fContext.fTypes.fHalf4) {
@@ -1304,7 +1305,7 @@
                     SK_ABORT("NOT YET IMPLEMENTED: automatic key handling for %s\n",
                              varType.displayName().c_str());
                 }
-                if (var.modifiers().fLayout.fWhen.fLength) {
+                if (var.modifiers().fLayout.fWhen.length()) {
                     this->write("}\n");
                 }
             }
diff --git a/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp b/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp
index ff2a234..69e6caf 100644
--- a/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLGLSLCodeGenerator.cpp
@@ -24,30 +24,8 @@
 
 namespace SkSL {
 
-void GLSLCodeGenerator::write(const char* s) {
-    if (s[0] == 0) {
-        return;
-    }
-    if (fAtLineStart) {
-        for (int i = 0; i < fIndentation; i++) {
-            fOut->writeText("    ");
-        }
-    }
-    fOut->writeText(s);
-    fAtLineStart = false;
-}
-
-void GLSLCodeGenerator::writeLine(const char* s) {
-    this->write(s);
-    this->writeLine();
-}
-
-void GLSLCodeGenerator::write(const String& s) {
-    this->write(s.c_str());
-}
-
 void GLSLCodeGenerator::write(StringFragment s) {
-    if (!s.fLength) {
+    if (!s.length()) {
         return;
     }
     if (fAtLineStart) {
@@ -55,15 +33,12 @@
             fOut->writeText("    ");
         }
     }
-    fOut->write(s.fChars, s.fLength);
+    fOut->write(s.data(), s.length());
     fAtLineStart = false;
 }
 
-void GLSLCodeGenerator::writeLine(const String& s) {
-    this->writeLine(s.c_str());
-}
-
-void GLSLCodeGenerator::writeLine() {
+void GLSLCodeGenerator::writeLine(StringFragment s) {
+    this->write(s);
     fOut->writeText(fLineEnding);
     fAtLineStart = true;
 }
@@ -74,13 +49,9 @@
     }
 }
 
-void GLSLCodeGenerator::writeExtension(const String& name) {
-    this->writeExtension(name, true);
-}
-
-void GLSLCodeGenerator::writeExtension(const String& name, bool require) {
+void GLSLCodeGenerator::writeExtension(StringFragment name, bool require) {
     fExtensions.writeText("#extension ");
-    fExtensions.write(name.c_str(), name.length());
+    fExtensions.write(name.data(), name.length());
     fExtensions.writeText(require ? " : require\n" : " : enable\n");
 }
 
@@ -145,14 +116,14 @@
                 return "uint";
             }
             else {
-                return type.name();
+                return String(type.name());
             }
             break;
         }
         case Type::TypeKind::kEnum:
             return "int";
         default:
-            return type.name();
+            return String(type.name());
     }
 }
 
diff --git a/src/sksl/codegen/SkSLGLSLCodeGenerator.h b/src/sksl/codegen/SkSLGLSLCodeGenerator.h
index 4ce2a4d..12ba53e 100644
--- a/src/sksl/codegen/SkSLGLSLCodeGenerator.h
+++ b/src/sksl/codegen/SkSLGLSLCodeGenerator.h
@@ -64,17 +64,9 @@
 protected:
     using Precedence = Operator::Precedence;
 
-    void write(const char* s);
-
-    void writeLine();
-
-    void writeLine(const char* s);
-
-    void write(const String& s);
-
     void write(StringFragment s);
 
-    void writeLine(const String& s);
+    void writeLine(StringFragment s = StringFragment());
 
     void finishLine();
 
@@ -88,9 +80,7 @@
 
     void writeType(const Type& type);
 
-    void writeExtension(const String& name);
-
-    void writeExtension(const String& name, bool require);
+    void writeExtension(StringFragment name, bool require = true);
 
     void writeInterfaceBlock(const InterfaceBlock& intf);
 
diff --git a/src/sksl/codegen/SkSLHCodeGenerator.cpp b/src/sksl/codegen/SkSLHCodeGenerator.cpp
index cf5795b..f665a9b 100644
--- a/src/sksl/codegen/SkSLHCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLHCodeGenerator.cpp
@@ -41,7 +41,7 @@
     if (ctype != Layout::CType::kDefault) {
         return Layout::CTypeToStr(ctype);
     }
-    return type.name();
+    return String(type.name());
 }
 
 Layout::CType HCodeGenerator::ParameterCType(const Context& context, const Type& type,
@@ -122,7 +122,8 @@
 bool HCodeGenerator::writeSection(const char* name, const char* prefix) {
     const Section* s = fSectionAndParameterHelper.getSection(name);
     if (s) {
-        this->writef("%s%.*s", prefix, (int)s->text().length(), s->text().data());
+        this->writef("%s", prefix);
+        this->writef("%.*s", (int)s->text().length(), s->text().data());
         return true;
     }
     return false;
diff --git a/src/sksl/codegen/SkSLMetalCodeGenerator.cpp b/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
index c818374..c8bc3b2 100644
--- a/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLMetalCodeGenerator.cpp
@@ -38,14 +38,14 @@
 class MetalCodeGenerator::GlobalStructVisitor {
 public:
     virtual ~GlobalStructVisitor() = default;
-    virtual void visitInterfaceBlock(const InterfaceBlock& block, const String& blockName) = 0;
-    virtual void visitTexture(const Type& type, const String& name) = 0;
-    virtual void visitSampler(const Type& type, const String& name) = 0;
+    virtual void visitInterfaceBlock(const InterfaceBlock& block, StringFragment blockName) = 0;
+    virtual void visitTexture(const Type& type, StringFragment name) = 0;
+    virtual void visitSampler(const Type& type, StringFragment name) = 0;
     virtual void visitVariable(const Variable& var, const Expression* value) = 0;
 };
 
-void MetalCodeGenerator::write(const char* s) {
-    if (!s[0]) {
+void MetalCodeGenerator::write(StringFragment s) {
+    if (s.empty()) {
         return;
     }
     if (fAtLineStart) {
@@ -53,24 +53,12 @@
             fOut->writeText("    ");
         }
     }
-    fOut->writeText(s);
+    fOut->writeText(String(s).c_str());
     fAtLineStart = false;
 }
 
-void MetalCodeGenerator::writeLine(const char* s) {
+void MetalCodeGenerator::writeLine(StringFragment s) {
     this->write(s);
-    this->writeLine();
-}
-
-void MetalCodeGenerator::write(const String& s) {
-    this->write(s.c_str());
-}
-
-void MetalCodeGenerator::writeLine(const String& s) {
-    this->writeLine(s.c_str());
-}
-
-void MetalCodeGenerator::writeLine() {
     fOut->writeText(fLineEnding);
     fAtLineStart = true;
 }
@@ -108,9 +96,9 @@
         default:
             if (type == *fContext.fTypes.fHalf) {
                 // FIXME - Currently only supporting floats in MSL to avoid type coercion issues.
-                return fContext.fTypes.fFloat->name();
+                return String(fContext.fTypes.fFloat->name());
             } else {
-                return type.name();
+                return String(type.name());
             }
     }
 }
@@ -1872,7 +1860,7 @@
     this->writeExpression(value, Precedence::kTopLevel);
 }
 
-void MetalCodeGenerator::writeName(const String& name) {
+void MetalCodeGenerator::writeName(StringFragment name) {
     if (fReservedWords.find(name) != fReservedWords.end()) {
         this->write("_"); // adding underscore before name to avoid conflict with reserved words
     }
@@ -2207,7 +2195,7 @@
         if (var.type().typeKind() == Type::TypeKind::kSampler) {
             // Samplers are represented as a "texture/sampler" duo in the global struct.
             visitor->visitTexture(var.type(), var.name());
-            visitor->visitSampler(var.type(), String(var.name()) + SAMPLER_SUFFIX);
+            visitor->visitSampler(var.type(), var.name() + SAMPLER_SUFFIX);
             continue;
         }
 
@@ -2222,7 +2210,7 @@
 void MetalCodeGenerator::writeGlobalStruct() {
     class : public GlobalStructVisitor {
     public:
-        void visitInterfaceBlock(const InterfaceBlock& block, const String& blockName) override {
+        void visitInterfaceBlock(const InterfaceBlock& block, StringFragment blockName) override {
             this->addElement();
             fCodeGen->write("    constant ");
             fCodeGen->write(block.typeName());
@@ -2230,7 +2218,7 @@
             fCodeGen->writeName(blockName);
             fCodeGen->write(";\n");
         }
-        void visitTexture(const Type& type, const String& name) override {
+        void visitTexture(const Type& type, StringFragment name) override {
             this->addElement();
             fCodeGen->write("    ");
             fCodeGen->writeType(type);
@@ -2238,7 +2226,7 @@
             fCodeGen->writeName(name);
             fCodeGen->write(";\n");
         }
-        void visitSampler(const Type&, const String& name) override {
+        void visitSampler(const Type&, StringFragment name) override {
             this->addElement();
             fCodeGen->write("    sampler ");
             fCodeGen->writeName(name);
@@ -2279,16 +2267,16 @@
     class : public GlobalStructVisitor {
     public:
         void visitInterfaceBlock(const InterfaceBlock& blockType,
-                                 const String& blockName) override {
+                                 StringFragment blockName) override {
             this->addElement();
             fCodeGen->write("&");
             fCodeGen->writeName(blockName);
         }
-        void visitTexture(const Type&, const String& name) override {
+        void visitTexture(const Type&, StringFragment name) override {
             this->addElement();
             fCodeGen->writeName(name);
         }
-        void visitSampler(const Type&, const String& name) override {
+        void visitSampler(const Type&, StringFragment name) override {
             this->addElement();
             fCodeGen->writeName(name);
         }
diff --git a/src/sksl/codegen/SkSLMetalCodeGenerator.h b/src/sksl/codegen/SkSLMetalCodeGenerator.h
index 47ff803..65517d5 100644
--- a/src/sksl/codegen/SkSLMetalCodeGenerator.h
+++ b/src/sksl/codegen/SkSLMetalCodeGenerator.h
@@ -83,15 +83,9 @@
     class GlobalStructVisitor;
     void visitGlobalStruct(GlobalStructVisitor* visitor);
 
-    void write(const char* s);
+    void write(StringFragment s);
 
-    void writeLine();
-
-    void writeLine(const char* s);
-
-    void write(const String& s);
-
-    void writeLine(const String& s);
+    void writeLine(StringFragment s = StringFragment());
 
     void finishLine();
 
@@ -149,7 +143,7 @@
 
     void writeVarInitializer(const Variable& var, const Expression& value);
 
-    void writeName(const String& name);
+    void writeName(StringFragment name);
 
     void writeVarDeclaration(const VarDeclaration& decl);
 
@@ -272,7 +266,7 @@
 
     int getUniformSet(const Modifiers& m);
 
-    std::unordered_set<String> fReservedWords;
+    std::unordered_set<StringFragment> fReservedWords;
     std::unordered_map<const Type::Field*, const InterfaceBlock*> fInterfaceBlockMap;
     std::unordered_map<const InterfaceBlock*, String> fInterfaceBlockNameMap;
     int fAnonInterfaceCount = 0;
diff --git a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
index ebf1554..5efd2c7 100644
--- a/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLPipelineStageCodeGenerator.cpp
@@ -57,10 +57,8 @@
 private:
     using Precedence = Operator::Precedence;
 
-    void write(const char* s);
-    void writeLine(const char* s = nullptr);
-    void write(const String& s);
     void write(StringFragment s);
+    void writeLine(StringFragment s = StringFragment());
 
     String typeName(const Type& type);
     void writeType(const Type& type);
@@ -126,23 +124,14 @@
     bool          fCastReturnsToHalf = false;
 };
 
-void PipelineStageCodeGenerator::write(const char* s) {
-    fBuffer->writeText(s);
-}
 
-void PipelineStageCodeGenerator::writeLine(const char* s) {
-    if (s) {
-        fBuffer->writeText(s);
-    }
-    fBuffer->writeText("\n");
-}
-
-void PipelineStageCodeGenerator::write(const String& s) {
+void PipelineStageCodeGenerator::write(StringFragment s) {
     fBuffer->write(s.data(), s.length());
 }
 
-void PipelineStageCodeGenerator::write(StringFragment s) {
-    fBuffer->write(s.fChars, s.fLength);
+void PipelineStageCodeGenerator::writeLine(StringFragment s) {
+    fBuffer->write(s.data(), s.length());
+    fBuffer->writeText("\n");
 }
 
 void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
@@ -285,7 +274,7 @@
         fCastReturnsToHalf = false;
     }
 
-    String fnName = decl.isMain() ? decl.name()
+    String fnName = decl.isMain() ? String(decl.name())
                                   : fCallbacks->getMangledName(String(decl.name()).c_str());
 
     // This is similar to decl.description(), but substitutes a mangled name, and handles modifiers
@@ -391,7 +380,7 @@
     }
 
     auto it = fStructNames.find(&type);
-    return it != fStructNames.end() ? it->second : type.name();
+    return it != fStructNames.end() ? it->second : String(type.name());
 }
 
 void PipelineStageCodeGenerator::writeType(const Type& type) {
diff --git a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
index 88b3e64..d6e1c58 100644
--- a/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp
@@ -273,9 +273,9 @@
     this->writeWord(word1, out);
 }
 
-void SPIRVCodeGenerator::writeString(const char* string, size_t length, OutputStream& out) {
-    out.write(string, length);
-    switch (length % 4) {
+void SPIRVCodeGenerator::writeString(StringFragment s, OutputStream& out) {
+    out.write(s.data(), s.length());
+    switch (s.length() % 4) {
         case 1:
             out.write8(0);
             [[fallthrough]];
@@ -291,24 +291,24 @@
 }
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, StringFragment string, OutputStream& out) {
-    this->writeOpCode(opCode, 1 + (string.fLength + 4) / 4, out);
-    this->writeString(string.fChars, string.fLength, out);
+    this->writeOpCode(opCode, 1 + (string.length() + 4) / 4, out);
+    this->writeString(string, out);
 }
 
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, StringFragment string,
                                           OutputStream& out) {
-    this->writeOpCode(opCode, 2 + (string.fLength + 4) / 4, out);
+    this->writeOpCode(opCode, 2 + (string.length() + 4) / 4, out);
     this->writeWord(word1, out);
-    this->writeString(string.fChars, string.fLength, out);
+    this->writeString(string, out);
 }
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
                                           StringFragment string, OutputStream& out) {
-    this->writeOpCode(opCode, 3 + (string.fLength + 4) / 4, out);
+    this->writeOpCode(opCode, 3 + (string.length() + 4) / 4, out);
     this->writeWord(word1, out);
     this->writeWord(word2, out);
-    this->writeString(string.fChars, string.fLength, out);
+    this->writeString(string, out);
 }
 
 void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
@@ -510,7 +510,7 @@
 
 SpvId SPIRVCodeGenerator::getType(const Type& rawType, const MemoryLayout& layout) {
     const Type& type = this->getActualType(rawType);
-    String key = type.name();
+    String key(type.name());
     if (type.isStruct() || type.isArray()) {
         key += to_string((int)layout.fStd);
 #ifdef SK_DEBUG
@@ -2065,7 +2065,7 @@
                                          /*invocations=*/-1, /*when=*/"", Layout::CType::kDefault),
                                   /*flags=*/0),
                         SKSL_RTHEIGHT_NAME, fContext.fTypes.fFloat.get());
-                StringFragment name("sksl_synthetic_uniforms");
+                String name("sksl_synthetic_uniforms");
                 std::unique_ptr<Type> intfStruct = Type::MakeStructType(/*offset=*/-1, name,
                                                                         fields);
                 int binding = fProgram.fConfig->fSettings.fRTHeightBinding;
@@ -2092,8 +2092,11 @@
                                                    intfStruct.get(),
                                                    /*builtin=*/false,
                                                    Variable::Storage::kGlobal));
-                InterfaceBlock intf(/*offset=*/-1, intfVar, name,
-                                    /*instanceName=*/"", /*arraySize=*/0,
+                InterfaceBlock intf(/*offset=*/-1,
+                                    intfVar,
+                                    name,
+                                    /*instanceName=*/"",
+                                    /*arraySize=*/0,
                                     std::make_shared<SymbolTable>(&fErrors, /*builtin=*/false));
 
                 fRTHeightStructId = this->writeInterfaceBlock(intf, false);
@@ -3063,7 +3066,8 @@
         fRTHeightStorageClass = storageClass;
         fields.emplace_back(Modifiers(), StringFragment(SKSL_RTHEIGHT_NAME),
                             fContext.fTypes.fFloat.get());
-        rtHeightStructType = Type::MakeStructType(type->fOffset, type->name(), std::move(fields));
+        rtHeightStructType = Type::MakeStructType(type->fOffset, String(type->name()),
+                                                  std::move(fields));
         type = rtHeightStructType.get();
     }
     SpvId typeId;
@@ -3626,7 +3630,7 @@
     this->writeCapabilities(out);
     this->writeInstruction(SpvOpExtInstImport, fGLSLExtendedInstructions, "GLSL.std.450", out);
     this->writeInstruction(SpvOpMemoryModel, SpvAddressingModelLogical, SpvMemoryModelGLSL450, out);
-    this->writeOpCode(SpvOpEntryPoint, (SpvId) (3 + (main->name().fLength + 4) / 4) +
+    this->writeOpCode(SpvOpEntryPoint, (SpvId) (3 + (main->name().length() + 4) / 4) +
                       (int32_t) interfaceVars.size(), out);
     switch (program.fConfig->fKind) {
         case ProgramKind::kVertex:
@@ -3643,7 +3647,7 @@
     }
     SpvId entryPoint = fFunctionMap[main];
     this->writeWord(entryPoint, out);
-    this->writeString(main->name().fChars, main->name().fLength, out);
+    this->writeString(main->name(), out);
     for (int var : interfaceVars) {
         this->writeWord(var, out);
     }
@@ -3658,7 +3662,7 @@
     }
     for (const ProgramElement* e : program.elements()) {
         if (e->is<Extension>()) {
-            this->writeInstruction(SpvOpSourceExtension, e->as<Extension>().name().c_str(), out);
+            this->writeInstruction(SpvOpSourceExtension, e->as<Extension>().name(), out);
         }
     }
 
diff --git a/src/sksl/codegen/SkSLSPIRVCodeGenerator.h b/src/sksl/codegen/SkSLSPIRVCodeGenerator.h
index c9e288b..d8c9b73 100644
--- a/src/sksl/codegen/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/codegen/SkSLSPIRVCodeGenerator.h
@@ -397,7 +397,7 @@
 
     void writeWord(int32_t word, OutputStream& out);
 
-    void writeString(const char* string, size_t length, OutputStream& out);
+    void writeString(StringFragment s, OutputStream& out);
 
     void writeLabel(SpvId id, OutputStream& out);
 
diff --git a/src/sksl/codegen/SkSLVMCodeGenerator.cpp b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
index d5c3f83..7aa2c02 100644
--- a/src/sksl/codegen/SkSLVMCodeGenerator.cpp
+++ b/src/sksl/codegen/SkSLVMCodeGenerator.cpp
@@ -1696,7 +1696,7 @@
         const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
         const Variable& var = decl.declaration()->as<VarDeclaration>().var();
         if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
-            gather_uniforms(info.get(), var.type(), var.name());
+            gather_uniforms(info.get(), var.type(), String(var.name()));
         }
     }
     return info;
diff --git a/src/sksl/ir/SkSLExtension.h b/src/sksl/ir/SkSLExtension.h
index 59f0b1a..cf8051a 100644
--- a/src/sksl/ir/SkSLExtension.h
+++ b/src/sksl/ir/SkSLExtension.h
@@ -19,11 +19,11 @@
 public:
     static constexpr Kind kProgramElementKind = Kind::kExtension;
 
-    Extension(int offset, String name)
+    Extension(int offset, StringFragment name)
         : INHERITED(offset, kProgramElementKind)
-        , fName(std::move(name)) {}
+        , fName(name) {}
 
-    const String& name() const {
+    StringFragment name() const {
         return fName;
     }
 
@@ -36,7 +36,7 @@
     }
 
 private:
-    String fName;
+    StringFragment fName;
 
     using INHERITED = ProgramElement;
 };
diff --git a/src/sksl/ir/SkSLFunctionDeclaration.cpp b/src/sksl/ir/SkSLFunctionDeclaration.cpp
index 347fa07..19059a6 100644
--- a/src/sksl/ir/SkSLFunctionDeclaration.cpp
+++ b/src/sksl/ir/SkSLFunctionDeclaration.cpp
@@ -319,7 +319,7 @@
         , fReturnType(returnType)
         , fBuiltin(builtin)
         , fIsMain(name == "main")
-        , fIntrinsicKind(builtin ? identify_intrinsic(name) : kNotIntrinsic) {}
+        , fIntrinsicKind(builtin ? identify_intrinsic(String(name)) : kNotIntrinsic) {}
 
 const FunctionDeclaration* FunctionDeclaration::Convert(const Context& context,
         SymbolTable& symbols, int offset, const Modifiers* modifiers,
@@ -353,10 +353,10 @@
 String FunctionDeclaration::mangledName() const {
     if ((this->isBuiltin() && !this->definition()) || this->isMain()) {
         // Builtins without a definition (like `sin` or `sqrt`) must use their real names.
-        return this->name();
+        return String(this->name());
     }
     // GLSL forbids two underscores in a row; add an extra character if necessary to avoid this.
-    const char* splitter = this->name().endsWith("_") ? "x_" : "_";
+    const char* splitter = this->name().ends_with("_") ? "x_" : "_";
     // Rename function to `funcname_returntypeparamtypes`.
     String result = this->name() + splitter + this->returnType().abbreviatedName();
     for (const Variable* p : this->parameters()) {
diff --git a/src/sksl/ir/SkSLInterfaceBlock.h b/src/sksl/ir/SkSLInterfaceBlock.h
index 8834834..5d92433 100644
--- a/src/sksl/ir/SkSLInterfaceBlock.h
+++ b/src/sksl/ir/SkSLInterfaceBlock.h
@@ -34,8 +34,8 @@
                    int arraySize, std::shared_ptr<SymbolTable> typeOwner)
     : INHERITED(offset, kProgramElementKind)
     , fVariable(var)
-    , fTypeName(std::move(typeName))
-    , fInstanceName(std::move(instanceName))
+    , fTypeName(typeName)
+    , fInstanceName(instanceName)
     , fArraySize(arraySize)
     , fTypeOwner(std::move(typeOwner)) {}
 
@@ -47,11 +47,11 @@
         fVariable = var;
     }
 
-    const String& typeName() const {
+    String typeName() const {
         return fTypeName;
     }
 
-    const String& instanceName() const {
+    String instanceName() const {
         return fInstanceName;
     }
 
diff --git a/src/sksl/ir/SkSLSetting.cpp b/src/sksl/ir/SkSLSetting.cpp
index b587762..83427de 100644
--- a/src/sksl/ir/SkSLSetting.cpp
+++ b/src/sksl/ir/SkSLSetting.cpp
@@ -65,13 +65,13 @@
         }
     }
 
-    const CapsLookupMethod* lookup(const String& name) const {
+    const CapsLookupMethod* lookup(StringFragment name) const {
         auto iter = fMap.find(name);
         return (iter != fMap.end()) ? iter->second.get() : nullptr;
     }
 
 private:
-    std::unordered_map<String, std::unique_ptr<CapsLookupMethod>> fMap;
+    std::unordered_map<StringFragment, std::unique_ptr<CapsLookupMethod>> fMap;
 };
 
 static const CapsLookupTable& caps_lookup_table() {
@@ -102,7 +102,7 @@
 
 }  // namespace
 
-static const Type* get_type(const Context& context, int offset, const String& name) {
+static const Type* get_type(const Context& context, int offset, StringFragment name) {
     if (const CapsLookupMethod* caps = caps_lookup_table().lookup(name)) {
         return caps->type(context);
     }
diff --git a/src/sksl/ir/SkSLSetting.h b/src/sksl/ir/SkSLSetting.h
index 425edab..9f93dc7 100644
--- a/src/sksl/ir/SkSLSetting.h
+++ b/src/sksl/ir/SkSLSetting.h
@@ -42,7 +42,7 @@
     }
 
     String description() const override {
-        return this->name();
+        return String(this->name());
     }
 
     bool hasProperty(Property property) const override {
diff --git a/src/sksl/ir/SkSLSymbolAlias.h b/src/sksl/ir/SkSLSymbolAlias.h
index 5276f06..8f7f79d 100644
--- a/src/sksl/ir/SkSLSymbolAlias.h
+++ b/src/sksl/ir/SkSLSymbolAlias.h
@@ -28,7 +28,7 @@
     }
 
     String description() const override {
-        return this->name();
+        return String(this->name());
     }
 
 private:
diff --git a/src/sksl/ir/SkSLSymbolTable.cpp b/src/sksl/ir/SkSLSymbolTable.cpp
index da00fd8..3d9bd7e 100644
--- a/src/sksl/ir/SkSLSymbolTable.cpp
+++ b/src/sksl/ir/SkSLSymbolTable.cpp
@@ -123,7 +123,7 @@
 
 const Type* SymbolTable::addArrayDimension(const Type* type, int arraySize) {
     if (arraySize != 0) {
-        String baseName = type->name();
+        String baseName(type->name());
         String arrayName = (arraySize != Type::kUnsizedArray)
                                    ? String::printf("%s[%d]", baseName.c_str(), arraySize)
                                    : String::printf("%s[]", baseName.c_str());
diff --git a/src/sksl/ir/SkSLType.cpp b/src/sksl/ir/SkSLType.cpp
index 3a13a23..2a5a8bc 100644
--- a/src/sksl/ir/SkSLType.cpp
+++ b/src/sksl/ir/SkSLType.cpp
@@ -205,15 +205,15 @@
     // This type actually needs to be cloned into the destination SymbolTable.
     switch (this->typeKind()) {
         case TypeKind::kArray:
-            return symbolTable->add(Type::MakeArrayType(this->name(), this->componentType(),
+            return symbolTable->add(Type::MakeArrayType(String(this->name()), this->componentType(),
                                                         this->columns()));
 
         case TypeKind::kStruct:
-            return symbolTable->add(Type::MakeStructType(this->fOffset, this->name(),
+            return symbolTable->add(Type::MakeStructType(this->fOffset, String(this->name()),
                                                          this->fields()));
 
         case TypeKind::kEnum:
-            return symbolTable->add(Type::MakeEnumType(this->name()));
+            return symbolTable->add(Type::MakeEnumType(String(this->name())));
 
         default:
             SkDEBUGFAILF("don't know how to clone type '%s'", this->description().c_str());
diff --git a/src/sksl/ir/SkSLType.h b/src/sksl/ir/SkSLType.h
index 331cfb4..cdbd5d1 100644
--- a/src/sksl/ir/SkSLType.h
+++ b/src/sksl/ir/SkSLType.h
@@ -136,7 +136,7 @@
     }
 
     String displayName() const {
-        return this->scalarTypeForLiteral().name();
+        return String(this->scalarTypeForLiteral().name());
     }
 
     String description() const override {
@@ -144,7 +144,7 @@
     }
 
     bool isPrivate() const {
-        return this->name().startsWith("$");
+        return this->name().starts_with("$");
     }
 
     bool operator==(const Type& other) const {
diff --git a/src/sksl/ir/SkSLUnresolvedFunction.h b/src/sksl/ir/SkSLUnresolvedFunction.h
index f1aad74..8bda69b 100644
--- a/src/sksl/ir/SkSLUnresolvedFunction.h
+++ b/src/sksl/ir/SkSLUnresolvedFunction.h
@@ -35,7 +35,7 @@
     }
 
     String description() const override {
-        return this->name();
+        return String(this->name());
     }
 
 private:
diff --git a/src/sksl/ir/SkSLVariableReference.cpp b/src/sksl/ir/SkSLVariableReference.cpp
index d6cb49f..86b1ba6 100644
--- a/src/sksl/ir/SkSLVariableReference.cpp
+++ b/src/sksl/ir/SkSLVariableReference.cpp
@@ -36,7 +36,7 @@
 }
 
 String VariableReference::description() const {
-    return this->variable()->name();
+    return String(this->variable()->name());
 }
 
 void VariableReference::setRefKind(RefKind refKind) {
diff --git a/tools/viewer/ParticlesSlide.cpp b/tools/viewer/ParticlesSlide.cpp
index 429a2dd..5a091d3 100644
--- a/tools/viewer/ParticlesSlide.cpp
+++ b/tools/viewer/ParticlesSlide.cpp
@@ -333,7 +333,7 @@
                     float* vals = data + uni.fSlot;
 
                     // Skip over builtin uniforms, to reduce clutter
-                    if (uni.fName == "dt" || uni.fName.startsWith("effect.")) {
+                    if (uni.fName == "dt" || uni.fName.starts_with("effect.")) {
                         continue;
                     }