• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/sksl/SkSLDehydrator.h"
9 
10 #include <map>
11 
12 #include "include/private/SkSLProgramElement.h"
13 #include "include/private/SkSLStatement.h"
14 #include "include/private/SkSLSymbol.h"
15 #include "src/sksl/SkSLRehydrator.h"
16 #include "src/sksl/ir/SkSLBinaryExpression.h"
17 #include "src/sksl/ir/SkSLBreakStatement.h"
18 #include "src/sksl/ir/SkSLConstructor.h"
19 #include "src/sksl/ir/SkSLConstructorArray.h"
20 #include "src/sksl/ir/SkSLConstructorArrayCast.h"
21 #include "src/sksl/ir/SkSLConstructorCompound.h"
22 #include "src/sksl/ir/SkSLConstructorCompoundCast.h"
23 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
24 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
25 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
26 #include "src/sksl/ir/SkSLConstructorSplat.h"
27 #include "src/sksl/ir/SkSLConstructorStruct.h"
28 #include "src/sksl/ir/SkSLContinueStatement.h"
29 #include "src/sksl/ir/SkSLDiscardStatement.h"
30 #include "src/sksl/ir/SkSLDoStatement.h"
31 #include "src/sksl/ir/SkSLExpressionStatement.h"
32 #include "src/sksl/ir/SkSLField.h"
33 #include "src/sksl/ir/SkSLFieldAccess.h"
34 #include "src/sksl/ir/SkSLForStatement.h"
35 #include "src/sksl/ir/SkSLFunctionCall.h"
36 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
37 #include "src/sksl/ir/SkSLFunctionDefinition.h"
38 #include "src/sksl/ir/SkSLIfStatement.h"
39 #include "src/sksl/ir/SkSLIndexExpression.h"
40 #include "src/sksl/ir/SkSLInlineMarker.h"
41 #include "src/sksl/ir/SkSLInterfaceBlock.h"
42 #include "src/sksl/ir/SkSLLiteral.h"
43 #include "src/sksl/ir/SkSLPostfixExpression.h"
44 #include "src/sksl/ir/SkSLPrefixExpression.h"
45 #include "src/sksl/ir/SkSLReturnStatement.h"
46 #include "src/sksl/ir/SkSLSetting.h"
47 #include "src/sksl/ir/SkSLStructDefinition.h"
48 #include "src/sksl/ir/SkSLSwitchCase.h"
49 #include "src/sksl/ir/SkSLSwitchStatement.h"
50 #include "src/sksl/ir/SkSLSwizzle.h"
51 #include "src/sksl/ir/SkSLSymbolAlias.h"
52 #include "src/sksl/ir/SkSLSymbolTable.h"
53 #include "src/sksl/ir/SkSLTernaryExpression.h"
54 #include "src/sksl/ir/SkSLUnresolvedFunction.h"
55 #include "src/sksl/ir/SkSLVarDeclarations.h"
56 #include "src/sksl/ir/SkSLVariable.h"
57 
58 #ifdef SKSL_STANDALONE
59 
60 namespace SkSL {
61 
62 static constexpr int HEADER_SIZE = 2;
63 
64 class AutoDehydratorSymbolTable {
65 public:
AutoDehydratorSymbolTable(Dehydrator * dehydrator,const std::shared_ptr<SymbolTable> & symbols)66     AutoDehydratorSymbolTable(Dehydrator* dehydrator, const std::shared_ptr<SymbolTable>& symbols)
67         : fDehydrator(dehydrator) {
68         dehydrator->fSymbolMap.emplace_back();
69         if (symbols) {
70             dehydrator->write(*symbols);
71         } else {
72             dehydrator->writeCommand(Rehydrator::kVoid_Command);
73         }
74     }
75 
~AutoDehydratorSymbolTable()76     ~AutoDehydratorSymbolTable() {
77         fDehydrator->fSymbolMap.pop_back();
78     }
79 
80 private:
81     Dehydrator* fDehydrator;
82 };
83 
write(Layout l)84 void Dehydrator::write(Layout l) {
85     if (l == Layout()) {
86         this->writeCommand(Rehydrator::kDefaultLayout_Command);
87     } else if (l == Layout::builtin(l.fBuiltin)) {
88         this->writeCommand(Rehydrator::kBuiltinLayout_Command);
89         this->writeS16(l.fBuiltin);
90     } else {
91         this->writeCommand(Rehydrator::kLayout_Command);
92         fBody.write32(l.fFlags);
93         this->writeS8(l.fLocation);
94         this->writeS8(l.fOffset);
95         this->writeS8(l.fBinding);
96         this->writeS8(l.fIndex);
97         this->writeS8(l.fSet);
98         this->writeS16(l.fBuiltin);
99         this->writeS8(l.fInputAttachmentIndex);
100     }
101 }
102 
write(Modifiers m)103 void Dehydrator::write(Modifiers m) {
104     if (m == Modifiers()) {
105         this->writeCommand(Rehydrator::kDefaultModifiers_Command);
106     } else {
107         if (m.fFlags <= 255) {
108             this->writeCommand(Rehydrator::kModifiers8Bit_Command);
109             this->write(m.fLayout);
110             this->writeU8(m.fFlags);
111         } else {
112             this->writeCommand(Rehydrator::kModifiers_Command);
113             this->write(m.fLayout);
114             this->writeS32(m.fFlags);
115         }
116     }
117 }
118 
write(skstd::string_view s)119 void Dehydrator::write(skstd::string_view s) {
120     this->write(String(s));
121 }
122 
write(String s)123 void Dehydrator::write(String s) {
124     auto found = fStrings.find(s);
125     int offset;
126     if (found == fStrings.end()) {
127         offset = fStringBuffer.str().length() + HEADER_SIZE;
128         fStrings.insert({ s, offset });
129         SkASSERT(s.length() <= 255);
130         fStringBreaks.add(fStringBuffer.bytesWritten());
131         fStringBuffer.write8(s.length());
132         fStringBuffer.writeString(s);
133     } else {
134         offset = found->second;
135     }
136     this->writeU16(offset);
137 }
138 
write(const Symbol & s)139 void Dehydrator::write(const Symbol& s) {
140     uint16_t id = this->symbolId(&s, false);
141     if (id) {
142         this->writeCommand(Rehydrator::kSymbolRef_Command);
143         this->writeU16(id);
144         return;
145     }
146     switch (s.kind()) {
147         case Symbol::Kind::kFunctionDeclaration: {
148             const FunctionDeclaration& f = s.as<FunctionDeclaration>();
149             this->writeCommand(Rehydrator::kFunctionDeclaration_Command);
150             this->writeId(&f);
151             this->write(f.modifiers());
152             this->write(f.name());
153             this->writeU8(f.parameters().size());
154             for (const Variable* p : f.parameters()) {
155                 this->writeU16(this->symbolId(p));
156             }
157             this->write(f.returnType());
158             break;
159         }
160         case Symbol::Kind::kSymbolAlias: {
161             const SymbolAlias& alias = s.as<SymbolAlias>();
162             this->writeCommand(Rehydrator::kSymbolAlias_Command);
163             this->writeId(&alias);
164             this->write(alias.name());
165             this->write(*alias.origSymbol());
166             break;
167         }
168         case Symbol::Kind::kUnresolvedFunction: {
169             const UnresolvedFunction& f = s.as<UnresolvedFunction>();
170             this->writeCommand(Rehydrator::kUnresolvedFunction_Command);
171             this->writeId(&f);
172             this->writeU8(f.functions().size());
173             for (const FunctionDeclaration* funcDecl : f.functions()) {
174                 this->write(*funcDecl);
175             }
176             break;
177         }
178         case Symbol::Kind::kType: {
179             const Type& t = s.as<Type>();
180             switch (t.typeKind()) {
181                 case Type::TypeKind::kArray:
182                     this->writeCommand(Rehydrator::kArrayType_Command);
183                     this->writeId(&t);
184                     this->write(t.componentType());
185                     this->writeS8(t.columns());
186                     break;
187                 case Type::TypeKind::kStruct:
188                     this->writeCommand(Rehydrator::kStructType_Command);
189                     this->writeId(&t);
190                     this->write(t.name());
191                     this->writeU8(t.fields().size());
192                     for (const Type::Field& f : t.fields()) {
193                         this->write(f.fModifiers);
194                         this->write(f.fName);
195                         this->write(*f.fType);
196                     }
197                     break;
198                 default:
199                     this->writeCommand(Rehydrator::kSystemType_Command);
200                     this->writeId(&t);
201                     this->write(t.name());
202                     break;
203             }
204             break;
205         }
206         case Symbol::Kind::kVariable: {
207             const Variable& v = s.as<Variable>();
208             this->writeCommand(Rehydrator::kVariable_Command);
209             this->writeId(&v);
210             this->write(v.modifiers());
211             this->write(v.name());
212             this->write(v.type());
213             this->writeU8((int8_t) v.storage());
214             break;
215         }
216         case Symbol::Kind::kField: {
217             const Field& f = s.as<Field>();
218             this->writeCommand(Rehydrator::kField_Command);
219             this->writeU16(this->symbolId(&f.owner()));
220             this->writeU8(f.fieldIndex());
221             break;
222         }
223         case Symbol::Kind::kExternal:
224             SkASSERT(false);
225             break;
226     }
227 }
228 
write(const SymbolTable & symbols)229 void Dehydrator::write(const SymbolTable& symbols) {
230     this->writeCommand(Rehydrator::kSymbolTable_Command);
231     this->writeU16(symbols.fOwnedSymbols.size());
232     for (const std::unique_ptr<const Symbol>& s : symbols.fOwnedSymbols) {
233         this->write(*s);
234     }
235     this->writeU16(symbols.fSymbols.count());
236     std::map<skstd::string_view, const Symbol*> ordered;
237     symbols.foreach([&](skstd::string_view name, const Symbol* symbol) {
238         ordered.insert({name, symbol});
239     });
240     for (std::pair<skstd::string_view, const Symbol*> p : ordered) {
241         SkDEBUGCODE(bool found = false;)
242         for (size_t i = 0; i < symbols.fOwnedSymbols.size(); ++i) {
243             if (symbols.fOwnedSymbols[i].get() == p.second) {
244                 fCommandBreaks.add(fBody.bytesWritten());
245                 this->writeU16(i);
246                 SkDEBUGCODE(found = true;)
247                 break;
248             }
249         }
250         SkASSERT(found);
251     }
252 }
253 
writeExpressionSpan(const SkSpan<const std::unique_ptr<Expression>> & span)254 void Dehydrator::writeExpressionSpan(const SkSpan<const std::unique_ptr<Expression>>& span) {
255     this->writeU8(span.size());
256     for (const auto& expr : span) {
257         this->write(expr.get());
258     }
259 }
260 
write(const Expression * e)261 void Dehydrator::write(const Expression* e) {
262     if (e) {
263         switch (e->kind()) {
264             case Expression::Kind::kBinary: {
265                 const BinaryExpression& b = e->as<BinaryExpression>();
266                 this->writeCommand(Rehydrator::kBinary_Command);
267                 this->write(b.left().get());
268                 this->writeU8((int) b.getOperator().kind());
269                 this->write(b.right().get());
270                 break;
271             }
272             case Expression::Kind::kChildCall:
273                 SkDEBUGFAIL("unimplemented--not expected to be used from within an include file");
274                 break;
275 
276             case Expression::Kind::kCodeString:
277                 SkDEBUGFAIL("shouldn't be able to receive kCodeString here");
278                 break;
279 
280             case Expression::Kind::kConstructorArray:
281                 this->writeCommand(Rehydrator::kConstructorArray_Command);
282                 this->write(e->type());
283                 this->writeExpressionSpan(e->as<ConstructorArray>().argumentSpan());
284                 break;
285 
286             case Expression::Kind::kConstructorArrayCast:
287                 this->writeCommand(Rehydrator::kConstructorArrayCast_Command);
288                 this->write(e->type());
289                 this->writeExpressionSpan(e->as<ConstructorArrayCast>().argumentSpan());
290                 break;
291 
292             case Expression::Kind::kConstructorCompound:
293                 this->writeCommand(Rehydrator::kConstructorCompound_Command);
294                 this->write(e->type());
295                 this->writeExpressionSpan(e->as<ConstructorCompound>().argumentSpan());
296                 break;
297 
298             case Expression::Kind::kConstructorCompoundCast:
299                 this->writeCommand(Rehydrator::kConstructorCompoundCast_Command);
300                 this->write(e->type());
301                 this->writeExpressionSpan(e->as<ConstructorCompoundCast>().argumentSpan());
302                 break;
303 
304             case Expression::Kind::kConstructorDiagonalMatrix:
305                 this->writeCommand(Rehydrator::kConstructorDiagonalMatrix_Command);
306                 this->write(e->type());
307                 this->writeExpressionSpan(e->as<ConstructorDiagonalMatrix>().argumentSpan());
308                 break;
309 
310             case Expression::Kind::kConstructorMatrixResize:
311                 this->writeCommand(Rehydrator::kConstructorMatrixResize_Command);
312                 this->write(e->type());
313                 this->writeExpressionSpan(e->as<ConstructorMatrixResize>().argumentSpan());
314                 break;
315 
316             case Expression::Kind::kConstructorScalarCast:
317                 this->writeCommand(Rehydrator::kConstructorScalarCast_Command);
318                 this->write(e->type());
319                 this->writeExpressionSpan(e->as<ConstructorScalarCast>().argumentSpan());
320                 break;
321 
322             case Expression::Kind::kConstructorSplat:
323                 this->writeCommand(Rehydrator::kConstructorSplat_Command);
324                 this->write(e->type());
325                 this->writeExpressionSpan(e->as<ConstructorSplat>().argumentSpan());
326                 break;
327 
328             case Expression::Kind::kConstructorStruct:
329                 this->writeCommand(Rehydrator::kConstructorStruct_Command);
330                 this->write(e->type());
331                 this->writeExpressionSpan(e->as<ConstructorStruct>().argumentSpan());
332                 break;
333 
334             case Expression::Kind::kExternalFunctionCall:
335             case Expression::Kind::kExternalFunctionReference:
336                 SkDEBUGFAIL("unimplemented--not expected to be used from within an include file");
337                 break;
338 
339             case Expression::Kind::kFieldAccess: {
340                 const FieldAccess& f = e->as<FieldAccess>();
341                 this->writeCommand(Rehydrator::kFieldAccess_Command);
342                 this->write(f.base().get());
343                 this->writeU8(f.fieldIndex());
344                 this->writeU8((int8_t) f.ownerKind());
345                 break;
346             }
347             case Expression::Kind::kFunctionCall: {
348                 const FunctionCall& f = e->as<FunctionCall>();
349                 this->writeCommand(Rehydrator::kFunctionCall_Command);
350                 this->write(f.type());
351                 this->writeId(&f.function());
352                 this->writeU8(f.arguments().size());
353                 for (const auto& a : f.arguments()) {
354                     this->write(a.get());
355                 }
356                 break;
357             }
358             case Expression::Kind::kIndex: {
359                 const IndexExpression& i = e->as<IndexExpression>();
360                 this->writeCommand(Rehydrator::kIndex_Command);
361                 this->write(i.base().get());
362                 this->write(i.index().get());
363                 break;
364             }
365             case Expression::Kind::kLiteral: {
366                 const Literal& l = e->as<Literal>();
367                 if (l.type().isFloat()) {
368                     float value = l.floatValue();
369                     int32_t floatBits;
370                     memcpy(&floatBits, &value, sizeof(floatBits));
371                     this->writeCommand(Rehydrator::kFloatLiteral_Command);
372                     this->write(l.type());
373                     this->writeS32(floatBits);
374                 } else if (l.type().isBoolean()) {
375                     this->writeCommand(Rehydrator::kBoolLiteral_Command);
376                     this->writeU8(l.boolValue());
377                 } else {
378                     SkASSERT(l.type().isInteger());
379                     this->writeCommand(Rehydrator::kIntLiteral_Command);
380                     this->write(l.type());
381                     this->writeS32(l.intValue());
382                 }
383                 break;
384             }
385             case Expression::Kind::kPostfix: {
386                 const PostfixExpression& p = e->as<PostfixExpression>();
387                 this->writeCommand(Rehydrator::kPostfix_Command);
388                 this->writeU8((int) p.getOperator().kind());
389                 this->write(p.operand().get());
390                 break;
391             }
392             case Expression::Kind::kPrefix: {
393                 const PrefixExpression& p = e->as<PrefixExpression>();
394                 this->writeCommand(Rehydrator::kPrefix_Command);
395                 this->writeU8((int) p.getOperator().kind());
396                 this->write(p.operand().get());
397                 break;
398             }
399             case Expression::Kind::kSetting: {
400                 const Setting& s = e->as<Setting>();
401                 this->writeCommand(Rehydrator::kSetting_Command);
402                 this->write(s.name());
403                 break;
404             }
405             case Expression::Kind::kSwizzle: {
406                 const Swizzle& s = e->as<Swizzle>();
407                 this->writeCommand(Rehydrator::kSwizzle_Command);
408                 this->write(s.base().get());
409                 this->writeU8(s.components().size());
410                 for (int c : s.components()) {
411                     this->writeU8(c);
412                 }
413                 break;
414             }
415             case Expression::Kind::kTernary: {
416                 const TernaryExpression& t = e->as<TernaryExpression>();
417                 this->writeCommand(Rehydrator::kTernary_Command);
418                 this->write(t.test().get());
419                 this->write(t.ifTrue().get());
420                 this->write(t.ifFalse().get());
421                 break;
422             }
423             case Expression::Kind::kVariableReference: {
424                 const VariableReference& v = e->as<VariableReference>();
425                 this->writeCommand(Rehydrator::kVariableReference_Command);
426                 this->writeId(v.variable());
427                 this->writeU8((int8_t) v.refKind());
428                 break;
429             }
430             case Expression::Kind::kFunctionReference:
431             case Expression::Kind::kMethodReference:
432             case Expression::Kind::kPoison:
433             case Expression::Kind::kTypeReference:
434                 SkDEBUGFAIL("this expression shouldn't appear in finished code");
435                 break;
436         }
437     } else {
438         this->writeCommand(Rehydrator::kVoid_Command);
439     }
440 }
441 
write(const Statement * s)442 void Dehydrator::write(const Statement* s) {
443     if (s) {
444         switch (s->kind()) {
445             case Statement::Kind::kBlock: {
446                 const Block& b = s->as<Block>();
447                 this->writeCommand(Rehydrator::kBlock_Command);
448                 AutoDehydratorSymbolTable symbols(this, b.symbolTable());
449                 this->writeU8(b.children().size());
450                 for (const std::unique_ptr<Statement>& blockStmt : b.children()) {
451                     this->write(blockStmt.get());
452                 }
453                 this->writeU8(b.isScope());
454                 break;
455             }
456             case Statement::Kind::kBreak:
457                 this->writeCommand(Rehydrator::kBreak_Command);
458                 break;
459             case Statement::Kind::kContinue:
460                 this->writeCommand(Rehydrator::kContinue_Command);
461                 break;
462             case Statement::Kind::kDiscard:
463                 this->writeCommand(Rehydrator::kDiscard_Command);
464                 break;
465             case Statement::Kind::kDo: {
466                 const DoStatement& d = s->as<DoStatement>();
467                 this->writeCommand(Rehydrator::kDo_Command);
468                 this->write(d.statement().get());
469                 this->write(d.test().get());
470                 break;
471             }
472             case Statement::Kind::kExpression: {
473                 const ExpressionStatement& e = s->as<ExpressionStatement>();
474                 this->writeCommand(Rehydrator::kExpressionStatement_Command);
475                 this->write(e.expression().get());
476                 break;
477             }
478             case Statement::Kind::kFor: {
479                 const ForStatement& f = s->as<ForStatement>();
480                 this->writeCommand(Rehydrator::kFor_Command);
481                 this->write(f.initializer().get());
482                 this->write(f.test().get());
483                 this->write(f.next().get());
484                 this->write(f.statement().get());
485                 this->write(*f.symbols());
486                 break;
487             }
488             case Statement::Kind::kIf: {
489                 const IfStatement& i = s->as<IfStatement>();
490                 this->writeCommand(Rehydrator::kIf_Command);
491                 this->writeU8(i.isStatic());
492                 this->write(i.test().get());
493                 this->write(i.ifTrue().get());
494                 this->write(i.ifFalse().get());
495                 break;
496             }
497             case Statement::Kind::kInlineMarker: {
498                 const InlineMarker& i = s->as<InlineMarker>();
499                 this->writeCommand(Rehydrator::kInlineMarker_Command);
500                 this->writeId(&i.function());
501                 break;
502             }
503             case Statement::Kind::kNop:
504                 SkDEBUGFAIL("unexpected--nop statement in finished code");
505                 break;
506             case Statement::Kind::kReturn: {
507                 const ReturnStatement& r = s->as<ReturnStatement>();
508                 this->writeCommand(Rehydrator::kReturn_Command);
509                 this->write(r.expression().get());
510                 break;
511             }
512             case Statement::Kind::kSwitch: {
513                 const SwitchStatement& ss = s->as<SwitchStatement>();
514                 this->writeCommand(Rehydrator::kSwitch_Command);
515                 this->writeU8(ss.isStatic());
516                 AutoDehydratorSymbolTable symbols(this, ss.symbols());
517                 this->write(ss.value().get());
518                 this->writeU8(ss.cases().size());
519                 for (const std::unique_ptr<Statement>& stmt : ss.cases()) {
520                     const SwitchCase& sc = stmt->as<SwitchCase>();
521                     this->write(sc.value().get());
522                     this->write(sc.statement().get());
523                 }
524                 break;
525             }
526             case Statement::Kind::kSwitchCase:
527                 SkDEBUGFAIL("SwitchCase statements shouldn't appear here");
528                 break;
529             case Statement::Kind::kVarDeclaration: {
530                 const VarDeclaration& v = s->as<VarDeclaration>();
531                 this->writeCommand(Rehydrator::kVarDeclaration_Command);
532                 this->writeU16(this->symbolId(&v.var()));
533                 this->write(v.baseType());
534                 this->writeS8(v.arraySize());
535                 this->write(v.value().get());
536                 break;
537             }
538         }
539     } else {
540         this->writeCommand(Rehydrator::kVoid_Command);
541     }
542 }
543 
write(const ProgramElement & e)544 void Dehydrator::write(const ProgramElement& e) {
545     switch (e.kind()) {
546         case ProgramElement::Kind::kExtension:
547             SkASSERT(false);
548             break;
549         case ProgramElement::Kind::kFunction: {
550             const FunctionDefinition& f = e.as<FunctionDefinition>();
551             this->writeCommand(Rehydrator::kFunctionDefinition_Command);
552             this->writeU16(this->symbolId(&f.declaration()));
553             this->write(f.body().get());
554             break;
555         }
556         case ProgramElement::Kind::kFunctionPrototype: {
557             // We don't need to emit function prototypes into the dehydrated data, because we don't
558             // ever need to re-emit the intrinsics files as raw GLSL/Metal. As long as the symbols
559             // exist in the symbol table, we're in good shape.
560             break;
561         }
562         case ProgramElement::Kind::kInterfaceBlock: {
563             const InterfaceBlock& i = e.as<InterfaceBlock>();
564             this->writeCommand(Rehydrator::kInterfaceBlock_Command);
565             this->write(i.variable());
566             this->write(i.typeName());
567             this->write(i.instanceName());
568             this->writeS8(i.arraySize());
569             break;
570         }
571         case ProgramElement::Kind::kModifiers:
572             SkASSERT(false);
573             break;
574         case ProgramElement::Kind::kStructDefinition: {
575             const StructDefinition& structDef = e.as<StructDefinition>();
576             this->writeCommand(Rehydrator::kStructDefinition_Command);
577             this->write(structDef.type());
578             break;
579         }
580         case ProgramElement::Kind::kGlobalVar: {
581             const GlobalVarDeclaration& v = e.as<GlobalVarDeclaration>();
582             this->writeCommand(Rehydrator::kVarDeclarations_Command);
583             this->write(v.declaration().get());
584             break;
585         }
586     }
587 }
588 
write(const std::vector<std::unique_ptr<ProgramElement>> & elements)589 void Dehydrator::write(const std::vector<std::unique_ptr<ProgramElement>>& elements) {
590     this->writeCommand(Rehydrator::kElements_Command);
591     for (const auto& e : elements) {
592         this->write(*e);
593     }
594     this->writeCommand(Rehydrator::kElementsComplete_Command);
595 }
596 
finish(OutputStream & out)597 void Dehydrator::finish(OutputStream& out) {
598     String stringBuffer = fStringBuffer.str();
599     String commandBuffer = fBody.str();
600 
601     out.write16(fStringBuffer.str().size());
602     fStringBufferStart = 2;
603     out.writeString(stringBuffer);
604     fCommandStart = fStringBufferStart + stringBuffer.size();
605     out.writeString(commandBuffer);
606 }
607 
prefixAtOffset(size_t byte)608 const char* Dehydrator::prefixAtOffset(size_t byte) {
609     if (byte >= fCommandStart) {
610         return fCommandBreaks.contains(byte - fCommandStart) ? "\n" : "";
611     }
612     if (byte >= fStringBufferStart) {
613         return fStringBreaks.contains(byte - fStringBufferStart) ? "\n" : "";
614     }
615     return "";
616 }
617 
618 } // namespace
619 
620 #endif
621