diff --git a/src/Desc.cc b/src/Desc.cc index 3497f73680..0def8cd8aa 100644 --- a/src/Desc.cc +++ b/src/Desc.cc @@ -40,6 +40,7 @@ ODesc::ODesc(DescType t, File* arg_f) indent_level = 0; is_short = false; want_quotes = false; + want_determinism = false; do_flush = true; include_stats = false; indent_with_spaces = 0; diff --git a/src/Desc.h b/src/Desc.h index a290e5b68d..23fb172ec6 100644 --- a/src/Desc.h +++ b/src/Desc.h @@ -47,6 +47,11 @@ public: bool WantQuotes() const { return want_quotes; } void SetQuotes(bool q) { want_quotes = q; } + // Whether to ensure deterministic output (for example, when + // describing TableVal's). + bool WantDeterminism() const { return want_determinism; } + void SetDeterminism(bool d) { want_determinism = d; } + // Whether we want to print statistics like access time and execution // count where available. bool IncludeStats() const { return include_stats; } @@ -122,7 +127,7 @@ public: Add("\n", 0); } - // Bypasses the escaping enabled via SetEscape(). + // Bypasses the escaping enabled via EnableEscaping(). void AddRaw(const char* s, int len) { AddBytesRaw(s, len); } void AddRaw(const std::string &s) { AddBytesRaw(s.data(), s.size()); } @@ -194,6 +199,7 @@ protected: bool escape; // escape unprintable characters in output? bool is_short; bool want_quotes; + bool want_determinism; int indent_with_spaces; diff --git a/src/Val.cc b/src/Val.cc index f1a2681167..5c7b388018 100644 --- a/src/Val.cc +++ b/src/Val.cc @@ -2334,6 +2334,9 @@ void TableVal::Describe(ODesc* d) const d->PushIndent(); } + bool determ = d->WantDeterminism(); + std::vector elem_descs; + auto iter = table_val->begin(); for ( int i = 0; i < n; ++i ) @@ -2347,7 +2350,10 @@ void TableVal::Describe(ODesc* d) const auto vl = table_hash->RecoverVals(*k); int dim = vl->Length(); - if ( i > 0 ) + ODesc intermediary_d; + ODesc* d_ptr = determ ? &intermediary_d : d; + + if ( ! determ && i > 0 ) { if ( ! d->IsBinary() ) d->Add(","); @@ -2358,42 +2364,67 @@ void TableVal::Describe(ODesc* d) const if ( d->IsReadable() ) { if ( dim != 1 || ! table_type->IsSet() ) - d->Add("["); + d_ptr->Add("["); } else { - d->Add(dim); - d->SP(); + d_ptr->Add(dim); + d_ptr->SP(); } - vl->Describe(d); + vl->Describe(d_ptr); if ( table_type->IsSet() ) { // We're a set, not a table. if ( d->IsReadable() ) if ( dim != 1 ) - d->AddSP("]"); + d_ptr->AddSP("]"); } else { if ( d->IsReadable() ) - d->AddSP("] ="); + d_ptr->AddSP("] ="); if ( v->GetVal() ) - v->GetVal()->Describe(d); + v->GetVal()->Describe(d_ptr); } if ( d->IsReadable() && ! d->IsShort() && d->IncludeStats() ) { - d->Add(" @"); - d->Add(util::detail::fmt_access_time(v->ExpireAccessTime())); + d_ptr->Add(" @"); + d_ptr->Add(util::detail::fmt_access_time(v->ExpireAccessTime())); } + if ( determ ) + elem_descs.emplace_back(d_ptr->Description()); + ++iter; } if ( iter != table_val->end() ) reporter->InternalError("hash table overflow in TableVal::Describe"); + if ( determ ) + { + sort(elem_descs.begin(), elem_descs.end()); + + iter = table_val->begin(); + bool did_elems = false; + + for ( const auto& ed : elem_descs ) + { + if ( did_elems ) + { + if ( ! d->IsBinary() ) + d->Add(","); + + d->NL(); + } + + d->Add(ed); + did_elems = true; + } + } + if ( d->IsPortable() || d->IsReadable() ) { d->PopIndent(); @@ -3501,7 +3532,8 @@ void describe_vals(const std::vector& vals, if ( i > offset && d->IsReadable() && d->Style() != RAW_STYLE ) d->Add(", "); - vals[i]->Describe(d); + if ( vals[i] ) + vals[i]->Describe(d); } }