<commit_before>// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

//#define ENABLE_TID_CHECK

#include "symtab.h"

#ifdef ENABLE_TID_CHECK
#include <pthread.h>
#endif
#include <string.h>

#include <unordered_map>

#include "log.h"
#include "strutil.h"
#include "var.h"

struct SymbolData {
  SymbolData() : gv(Var::Undefined()) {}

  Var* gv;
};

vector<string*>* g_symbols;
static vector<SymbolData> g_symbol_data;

Symbol kEmptySym;
Symbol kShellSym;
Symbol kAllowRulesSym;
Symbol kKatiReadonlySym;
Symbol kVariablesSym;
Symbol kKatiSymbolsSym;

Symbol::Symbol(int v) : v_(v) {}

Var* Symbol::PeekGlobalVar() const {
  if (static_cast<size_t>(v_) >= g_symbol_data.size()) {
    return Var::Undefined();
  }
  return g_symbol_data[v_].gv;
}

Var* Symbol::GetGlobalVar() const {
  if (static_cast<size_t>(v_) >= g_symbol_data.size()) {
    g_symbol_data.resize(v_ + 1);
  }
  Var* v = g_symbol_data[v_].gv;
  if (v->Origin() == VarOrigin::ENVIRONMENT ||
      v->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE) {
    Vars::add_used_env_vars(*this);
  }
  return v;
}

void Symbol::SetGlobalVar(Var* v, bool is_override, bool* readonly) const {
  if (static_cast<size_t>(v_) >= g_symbol_data.size()) {
    g_symbol_data.resize(v_ + 1);
  }
  Var* orig = g_symbol_data[v_].gv;
  if (orig->ReadOnly()) {
    if (readonly != nullptr)
      *readonly = true;
    else
      ERROR("*** cannot assign to readonly variable: %s", c_str());
    return;
  } else if (readonly != nullptr) {
    *readonly = false;
  }
  if (!is_override && (orig->Origin() == VarOrigin::OVERRIDE ||
                       orig->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE)) {
    return;
  }
  if (orig->Origin() == VarOrigin::COMMAND_LINE &&
      v->Origin() == VarOrigin::FILE) {
    return;
  }
  if (orig->Origin() == VarOrigin::AUTOMATIC) {
    ERROR("overriding automatic variable is not implemented yet");
  }
  if (orig->IsDefined())
    delete orig;
  g_symbol_data[v_].gv = v;
}

ScopedGlobalVar::ScopedGlobalVar(Symbol name, Var* var)
    : name_(name), orig_(NULL) {
  orig_ = name.GetGlobalVar();
  g_symbol_data[name_.val()].gv = var;
}

ScopedGlobalVar::~ScopedGlobalVar() {
  g_symbol_data[name_.val()].gv = orig_;
}

class Symtab {
 public:
  Symtab() {
#ifdef ENABLE_TID_CHECK
    tid_ = pthread_self();
#endif

    CHECK(g_symbols == NULL);
    g_symbols = &symbols_;

    Symbol s = InternImpl("");
    CHECK(s.v_ == 0);
    CHECK(Intern("") == s);
    char b[2];
    b[1] = 0;
    for (int i = 1; i < 256; i++) {
      b[0] = i;
      s = InternImpl(b);
      CHECK(s.val() == i);
    }

    kEmptySym = Intern("");
    kShellSym = Intern("SHELL");
    kAllowRulesSym = Intern(".KATI_ALLOW_RULES");
    kKatiReadonlySym = Intern(".KATI_READONLY");
    kVariablesSym = Intern(".VARIABLES");
    kVariablesSym.SetGlobalVar(new VariableNamesVar(".VARIABLES", true), false,
                               nullptr);
    kKatiSymbolsSym = Intern(".KATI_SYMBOLS");
    kKatiSymbolsSym.SetGlobalVar(new VariableNamesVar(".KATI_SYMBOLS", false),
                                 false, nullptr);
  }

  ~Symtab() {
    LOG_STAT("%zu symbols", symbols_.size());
    for (string* s : symbols_)
      delete s;
  }

  Symbol InternImpl(StringPiece s) {
    auto found = symtab_.find(s);
    if (found != symtab_.end()) {
      return found->second;
    }
    symbols_.push_back(new string(s.data(), s.size()));
    Symbol sym = Symbol(symtab_.size());
    bool ok = symtab_.emplace(*symbols_.back(), sym).second;
    CHECK(ok);
    return sym;
  }

  Symbol Intern(StringPiece s) {
#ifdef ENABLE_TID_CHECK
    if (tid_ != pthread_self())
      abort();
#endif

    if (s.size() <= 1) {
      return Symbol(s.empty() ? 0 : (unsigned char)s[0]);
    }
    return InternImpl(s);
  }

  vector<StringPiece> GetSymbolNames(std::function<bool(Var*)> const& filter) {
    vector<StringPiece> result;
    for (auto entry : symtab_) {
      Var* var = entry.second.PeekGlobalVar();
      // The symbol table contains all interned strings, not just variables
      // which have been defined.
      if (!var->IsDefined()) {
        continue;
      }
      if (filter(var)) {
        result.push_back(entry.first);
      }
    }
    return result;
  }

 private:
  unordered_map<StringPiece, Symbol> symtab_;
  vector<string*> symbols_;
#ifdef ENABLE_TID_CHECK
  pthread_t tid_;
#endif
};

static Symtab* g_symtab;

void InitSymtab() {
  g_symtab = new Symtab;
}

void QuitSymtab() {
  delete g_symtab;
}

Symbol Intern(StringPiece s) {
  return g_symtab->Intern(s);
}

string JoinSymbols(const vector<Symbol>& syms, const char* sep) {
  vector<string> strs;
  for (Symbol s : syms) {
    strs.push_back(s.str());
  }
  return JoinStrings(strs, sep);
}

vector<StringPiece> GetSymbolNames(std::function<bool(Var*)> const& filter) {
  return g_symtab->GetSymbolNames(filter);
}
<commit_msg>symtab: JoinSymbols: reserve intermediate vector size<commit_after>// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

//#define ENABLE_TID_CHECK

#include "symtab.h"

#ifdef ENABLE_TID_CHECK
#include <pthread.h>
#endif
#include <string.h>

#include <unordered_map>

#include "log.h"
#include "strutil.h"
#include "var.h"

struct SymbolData {
  SymbolData() : gv(Var::Undefined()) {}

  Var* gv;
};

vector<string*>* g_symbols;
static vector<SymbolData> g_symbol_data;

Symbol kEmptySym;
Symbol kShellSym;
Symbol kAllowRulesSym;
Symbol kKatiReadonlySym;
Symbol kVariablesSym;
Symbol kKatiSymbolsSym;

Symbol::Symbol(int v) : v_(v) {}

Var* Symbol::PeekGlobalVar() const {
  if (static_cast<size_t>(v_) >= g_symbol_data.size()) {
    return Var::Undefined();
  }
  return g_symbol_data[v_].gv;
}

Var* Symbol::GetGlobalVar() const {
  if (static_cast<size_t>(v_) >= g_symbol_data.size()) {
    g_symbol_data.resize(v_ + 1);
  }
  Var* v = g_symbol_data[v_].gv;
  if (v->Origin() == VarOrigin::ENVIRONMENT ||
      v->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE) {
    Vars::add_used_env_vars(*this);
  }
  return v;
}

void Symbol::SetGlobalVar(Var* v, bool is_override, bool* readonly) const {
  if (static_cast<size_t>(v_) >= g_symbol_data.size()) {
    g_symbol_data.resize(v_ + 1);
  }
  Var* orig = g_symbol_data[v_].gv;
  if (orig->ReadOnly()) {
    if (readonly != nullptr)
      *readonly = true;
    else
      ERROR("*** cannot assign to readonly variable: %s", c_str());
    return;
  } else if (readonly != nullptr) {
    *readonly = false;
  }
  if (!is_override && (orig->Origin() == VarOrigin::OVERRIDE ||
                       orig->Origin() == VarOrigin::ENVIRONMENT_OVERRIDE)) {
    return;
  }
  if (orig->Origin() == VarOrigin::COMMAND_LINE &&
      v->Origin() == VarOrigin::FILE) {
    return;
  }
  if (orig->Origin() == VarOrigin::AUTOMATIC) {
    ERROR("overriding automatic variable is not implemented yet");
  }
  if (orig->IsDefined())
    delete orig;
  g_symbol_data[v_].gv = v;
}

ScopedGlobalVar::ScopedGlobalVar(Symbol name, Var* var)
    : name_(name), orig_(NULL) {
  orig_ = name.GetGlobalVar();
  g_symbol_data[name_.val()].gv = var;
}

ScopedGlobalVar::~ScopedGlobalVar() {
  g_symbol_data[name_.val()].gv = orig_;
}

class Symtab {
 public:
  Symtab() {
#ifdef ENABLE_TID_CHECK
    tid_ = pthread_self();
#endif

    CHECK(g_symbols == NULL);
    g_symbols = &symbols_;

    Symbol s = InternImpl("");
    CHECK(s.v_ == 0);
    CHECK(Intern("") == s);
    char b[2];
    b[1] = 0;
    for (int i = 1; i < 256; i++) {
      b[0] = i;
      s = InternImpl(b);
      CHECK(s.val() == i);
    }

    kEmptySym = Intern("");
    kShellSym = Intern("SHELL");
    kAllowRulesSym = Intern(".KATI_ALLOW_RULES");
    kKatiReadonlySym = Intern(".KATI_READONLY");
    kVariablesSym = Intern(".VARIABLES");
    kVariablesSym.SetGlobalVar(new VariableNamesVar(".VARIABLES", true), false,
                               nullptr);
    kKatiSymbolsSym = Intern(".KATI_SYMBOLS");
    kKatiSymbolsSym.SetGlobalVar(new VariableNamesVar(".KATI_SYMBOLS", false),
                                 false, nullptr);
  }

  ~Symtab() {
    LOG_STAT("%zu symbols", symbols_.size());
    for (string* s : symbols_)
      delete s;
  }

  Symbol InternImpl(StringPiece s) {
    auto found = symtab_.find(s);
    if (found != symtab_.end()) {
      return found->second;
    }
    symbols_.push_back(new string(s.data(), s.size()));
    Symbol sym = Symbol(symtab_.size());
    bool ok = symtab_.emplace(*symbols_.back(), sym).second;
    CHECK(ok);
    return sym;
  }

  Symbol Intern(StringPiece s) {
#ifdef ENABLE_TID_CHECK
    if (tid_ != pthread_self())
      abort();
#endif

    if (s.size() <= 1) {
      return Symbol(s.empty() ? 0 : (unsigned char)s[0]);
    }
    return InternImpl(s);
  }

  vector<StringPiece> GetSymbolNames(std::function<bool(Var*)> const& filter) {
    vector<StringPiece> result;
    for (auto entry : symtab_) {
      Var* var = entry.second.PeekGlobalVar();
      // The symbol table contains all interned strings, not just variables
      // which have been defined.
      if (!var->IsDefined()) {
        continue;
      }
      if (filter(var)) {
        result.push_back(entry.first);
      }
    }
    return result;
  }

 private:
  unordered_map<StringPiece, Symbol> symtab_;
  vector<string*> symbols_;
#ifdef ENABLE_TID_CHECK
  pthread_t tid_;
#endif
};

static Symtab* g_symtab;

void InitSymtab() {
  g_symtab = new Symtab;
}

void QuitSymtab() {
  delete g_symtab;
}

Symbol Intern(StringPiece s) {
  return g_symtab->Intern(s);
}

string JoinSymbols(const vector<Symbol>& syms, const char* sep) {
  vector<string> strs;
  strs.reserve(syms.size());
  for (Symbol s : syms) {
    strs.push_back(s.str());
  }
  return JoinStrings(strs, sep);
}

vector<StringPiece> GetSymbolNames(std::function<bool(Var*)> const& filter) {
  return g_symtab->GetSymbolNames(filter);
}
<|endoftext|>


<commit_before>// $Header$

#include "TPCData.h"

#include <Alieve/TPCSectorData.h>

#include <AliSimDigits.h>
#include <AliTPCParam.h>
#include <AliTPCRawStream.h>
#include <AliTPCRawStreamOld.h>
#include <TTree.h>

using namespace Reve;
using namespace Alieve;

//______________________________________________________________________
// TPCData
//
// A central manager for TPC data of an event.  Can read digits (from
// a tree: LoadDigits()) and raw-data (via AliRawReader: LoadRaw()).
//
// The sector data is stored in 36 TPCSectorData objects.
// Sectors 0 - 17: +z side, 18 - 35: -z side.
// No separation of inner/outer segments, use row numbers for addressing.
//
// Threshold application and pedestal subtraction can be performed at
// load time: use SetLoadThreshold(thresh) and SetLoadPedestal(ped).
//
// For raw-data (loaded using LoadRaw) pedestals can be calculated
// automatically per pad. Use SetAutoPedestal(kTRUE) to activate it.
// You might still want to set load threshold (default iz zero).
//

ClassImp(TPCData)

TPCData::TPCData() :
  fSectors(36), fSectorBlockSize(65536),
  fLoadThreshold(0), fLoadPedestal(0), fAutoPedestal(kFALSE)
{
  TPCSectorData::InitStatics();
}

TPCData::~TPCData()
{
  DeleteAllSectors();
}

/**************************************************************************/

void TPCData::CreateSector(Int_t sector)
{
  if(fSectors[sector] == 0)
    fSectors[sector] = new TPCSectorData(sector, fSectorBlockSize);
}

void TPCData::CreateAllSectors()
{
  for(Int_t s=0; s<36; ++s)
    CreateSector(s);
}

void TPCData::DropAllSectors()
{
  for(Int_t s=0; s<36; ++s) {
    if(fSectors[s] != 0)
      fSectors[s]->DropData();
  }
}

void TPCData::DeleteAllSectors()
{
  for(Int_t s=0; s<36; ++s) {
    delete fSectors[s];
    fSectors[s] = 0;
  }
}

/**************************************************************************/

TPCSectorData* TPCData::GetSectorData(Int_t sector, Bool_t spawnSectors)
{
  if(sector < 0 || sector > 35) return 0;
  if(fSectors[sector] == 0 && spawnSectors)
    CreateSector(sector);
  return fSectors[sector];
}

/**************************************************************************/

void TPCData::LoadDigits(TTree* tree, Bool_t spawnSectors)
{
  // Load data from TTree of AliSimDigits.
  // If spawnSectors is false only sectors that have been created previously
  // via CreateSector() are loaded.
  // If spawnSectors is true sectors are created if data for them is encountered.

  AliSimDigits digit, *digitPtr = &digit;
  tree->GetBranch("Segment")->SetAddress(&digitPtr);

  Int_t sector, row, pad, curPad;
  Short_t time, signal;
  Bool_t  inFill = kFALSE;
  TPCSectorData* secData = 0;

  Int_t numEnt = (Int_t) tree->GetEntries();
  for (Int_t ent=0; ent<numEnt; ent++) {
    tree->GetEntry(ent);
    Alieve::TPCSectorData::GetParam().AdjustSectorRow(digit.GetID(), sector, row);
    if(sector >= 36) {
      sector -= 36;
      row    += TPCSectorData::GetInnSeg().GetNRows();
    }
    secData = GetSectorData(sector, spawnSectors);
    if(secData == 0)
      continue;

    if(digit.First() == kFALSE)
      continue;
    curPad = -1;
    do {
      pad    = digit.CurrentColumn();
      time   = digit.CurrentRow();
      signal = digit.CurrentDigit();

      if(pad != curPad) {
	if(inFill)
	  secData->EndPad(fAutoPedestal, fLoadThreshold);
	secData->BeginPad(row, pad, kFALSE);
	curPad = pad;
	inFill = kTRUE;
      }
      if(signal > fLoadThreshold)
	secData->RegisterData(time, signal - fLoadPedestal);

    } while (digit.Next());
    if(inFill) {
      secData->EndPad(fAutoPedestal, fLoadThreshold);
      inFill = kFALSE;
    }
  }
}

void TPCData::LoadRaw(AliTPCRawStream& input, Bool_t spawnSectors, Bool_t warn)
{
  // Load data from AliTPCRawStream.
  // If spawnSectors is false only sectors that have been created previously
  // via CreateSector() are loaded.
  // If spawnSectors is true sectors are created if data for them is encountered.

  static const Exc_t eH("TPCData::LoadRaw ");

  Int_t   sector = -1, row = -1, pad = -1, rowOffset = 0;
  Short_t time,  signal;
  Bool_t  inFill   = kFALSE;
  Short_t lastTime = 9999;
  Bool_t  lastTimeWarn = kFALSE;
  TPCSectorData* secData = 0;

  Short_t threshold = fLoadThreshold;

  while (input.Next()) {
    if (input.IsNewSector()) {
      if(inFill) {
	secData->EndPad(fAutoPedestal, threshold);
	inFill = kFALSE;
      }
      sector = input.GetSector();
      if(sector >= 36) {
	sector -= 36;
	rowOffset = TPCSectorData::GetInnSeg().GetNRows();
      } else {
	rowOffset = 0;
      }
      secData = GetSectorData(sector, spawnSectors);
    }
    if (secData == 0)
      continue;

    if (input.IsNewPad()) {
      if(inFill) {
	secData->EndPad(fAutoPedestal, threshold);
	inFill = kFALSE;
      }
      row = input.GetRow() + rowOffset;
      pad = input.GetPad();

      if(pad >= TPCSectorData::GetNPadsInRow(row)) {
	if(warn) {
	  Warning(eH.Data(), "pad out of range (row=%d, pad=%d, maxpad=%d).",
		  row, pad, TPCSectorData::GetNPadsInRow(row));
	}
	continue;
      }

      TPCSectorData::PadRowHack* prh = secData->GetPadRowHack(row, pad);
      if(prh != 0) {
	printf ("hakahaka s=%d, r=%d, p=%d\n", sector, row, pad);
	threshold = prh->fThrExt + Short_t(prh->fThrFac*fLoadThreshold);
      } else {
	threshold = fLoadThreshold;
      }

      secData->BeginPad(row, pad, kTRUE);
      inFill   = kTRUE;
      lastTime = 1024;  lastTimeWarn = kFALSE;
    }

    time   = input.GetTime();
    signal = input.GetSignal();
    if(time >= lastTime) {
      if(lastTimeWarn == kFALSE) {
	if(warn)
	  Warning(eH.Data(), "time out of order (row=%d, pad=%d, time=%d, lastTime=%d).",
		  row, pad, time, lastTime);
        lastTimeWarn = kTRUE;
      }
      continue;
    }
    lastTime = time;
    if(fAutoPedestal) {
      secData->RegisterData(time, signal);
    } else {
      if(signal > threshold)
	secData->RegisterData(time, signal - fLoadPedestal);
    }
  }

  if(inFill) {
    secData->EndPad(fAutoPedestal, threshold);
    inFill = kFALSE;
  }
}
<commit_msg>Removed stale printout.<commit_after>// $Header$

#include "TPCData.h"

#include <Alieve/TPCSectorData.h>

#include <AliSimDigits.h>
#include <AliTPCParam.h>
#include <AliTPCRawStream.h>
#include <AliTPCRawStreamOld.h>
#include <TTree.h>

using namespace Reve;
using namespace Alieve;

//______________________________________________________________________
// TPCData
//
// A central manager for TPC data of an event.  Can read digits (from
// a tree: LoadDigits()) and raw-data (via AliRawReader: LoadRaw()).
//
// The sector data is stored in 36 TPCSectorData objects.
// Sectors 0 - 17: +z side, 18 - 35: -z side.
// No separation of inner/outer segments, use row numbers for addressing.
//
// Threshold application and pedestal subtraction can be performed at
// load time: use SetLoadThreshold(thresh) and SetLoadPedestal(ped).
//
// For raw-data (loaded using LoadRaw) pedestals can be calculated
// automatically per pad. Use SetAutoPedestal(kTRUE) to activate it.
// You might still want to set load threshold (default iz zero).
//

ClassImp(TPCData)

TPCData::TPCData() :
  fSectors(36), fSectorBlockSize(65536),
  fLoadThreshold(0), fLoadPedestal(0), fAutoPedestal(kFALSE)
{
  TPCSectorData::InitStatics();
}

TPCData::~TPCData()
{
  DeleteAllSectors();
}

/**************************************************************************/

void TPCData::CreateSector(Int_t sector)
{
  if(fSectors[sector] == 0)
    fSectors[sector] = new TPCSectorData(sector, fSectorBlockSize);
}

void TPCData::CreateAllSectors()
{
  for(Int_t s=0; s<36; ++s)
    CreateSector(s);
}

void TPCData::DropAllSectors()
{
  for(Int_t s=0; s<36; ++s) {
    if(fSectors[s] != 0)
      fSectors[s]->DropData();
  }
}

void TPCData::DeleteAllSectors()
{
  for(Int_t s=0; s<36; ++s) {
    delete fSectors[s];
    fSectors[s] = 0;
  }
}

/**************************************************************************/

TPCSectorData* TPCData::GetSectorData(Int_t sector, Bool_t spawnSectors)
{
  if(sector < 0 || sector > 35) return 0;
  if(fSectors[sector] == 0 && spawnSectors)
    CreateSector(sector);
  return fSectors[sector];
}

/**************************************************************************/

void TPCData::LoadDigits(TTree* tree, Bool_t spawnSectors)
{
  // Load data from TTree of AliSimDigits.
  // If spawnSectors is false only sectors that have been created previously
  // via CreateSector() are loaded.
  // If spawnSectors is true sectors are created if data for them is encountered.

  AliSimDigits digit, *digitPtr = &digit;
  tree->GetBranch("Segment")->SetAddress(&digitPtr);

  Int_t sector, row, pad, curPad;
  Short_t time, signal;
  Bool_t  inFill = kFALSE;
  TPCSectorData* secData = 0;

  Int_t numEnt = (Int_t) tree->GetEntries();
  for (Int_t ent=0; ent<numEnt; ent++) {
    tree->GetEntry(ent);
    Alieve::TPCSectorData::GetParam().AdjustSectorRow(digit.GetID(), sector, row);
    if(sector >= 36) {
      sector -= 36;
      row    += TPCSectorData::GetInnSeg().GetNRows();
    }
    secData = GetSectorData(sector, spawnSectors);
    if(secData == 0)
      continue;

    if(digit.First() == kFALSE)
      continue;
    curPad = -1;
    do {
      pad    = digit.CurrentColumn();
      time   = digit.CurrentRow();
      signal = digit.CurrentDigit();

      if(pad != curPad) {
	if(inFill)
	  secData->EndPad(fAutoPedestal, fLoadThreshold);
	secData->BeginPad(row, pad, kFALSE);
	curPad = pad;
	inFill = kTRUE;
      }
      if(signal > fLoadThreshold)
	secData->RegisterData(time, signal - fLoadPedestal);

    } while (digit.Next());
    if(inFill) {
      secData->EndPad(fAutoPedestal, fLoadThreshold);
      inFill = kFALSE;
    }
  }
}

void TPCData::LoadRaw(AliTPCRawStream& input, Bool_t spawnSectors, Bool_t warn)
{
  // Load data from AliTPCRawStream.
  // If spawnSectors is false only sectors that have been created previously
  // via CreateSector() are loaded.
  // If spawnSectors is true sectors are created if data for them is encountered.

  static const Exc_t eH("TPCData::LoadRaw ");

  Int_t   sector = -1, row = -1, pad = -1, rowOffset = 0;
  Short_t time,  signal;
  Bool_t  inFill   = kFALSE;
  Short_t lastTime = 9999;
  Bool_t  lastTimeWarn = kFALSE;
  TPCSectorData* secData = 0;

  Short_t threshold = fLoadThreshold;

  while (input.Next()) {
    if (input.IsNewSector()) {
      if(inFill) {
	secData->EndPad(fAutoPedestal, threshold);
	inFill = kFALSE;
      }
      sector = input.GetSector();
      if(sector >= 36) {
	sector -= 36;
	rowOffset = TPCSectorData::GetInnSeg().GetNRows();
      } else {
	rowOffset = 0;
      }
      secData = GetSectorData(sector, spawnSectors);
    }
    if (secData == 0)
      continue;

    if (input.IsNewPad()) {
      if(inFill) {
	secData->EndPad(fAutoPedestal, threshold);
	inFill = kFALSE;
      }
      row = input.GetRow() + rowOffset;
      pad = input.GetPad();

      if(pad >= TPCSectorData::GetNPadsInRow(row)) {
	if(warn) {
	  Warning(eH.Data(), "pad out of range (row=%d, pad=%d, maxpad=%d).",
		  row, pad, TPCSectorData::GetNPadsInRow(row));
	}
	continue;
      }

      TPCSectorData::PadRowHack* prh = secData->GetPadRowHack(row, pad);
      if(prh != 0) {
	threshold = prh->fThrExt + Short_t(prh->fThrFac*fLoadThreshold);
      } else {
	threshold = fLoadThreshold;
      }

      secData->BeginPad(row, pad, kTRUE);
      inFill   = kTRUE;
      lastTime = 1024;  lastTimeWarn = kFALSE;
    }

    time   = input.GetTime();
    signal = input.GetSignal();
    if(time >= lastTime) {
      if(lastTimeWarn == kFALSE) {
	if(warn)
	  Warning(eH.Data(), "time out of order (row=%d, pad=%d, time=%d, lastTime=%d).",
		  row, pad, time, lastTime);
        lastTimeWarn = kTRUE;
      }
      continue;
    }
    lastTime = time;
    if(fAutoPedestal) {
      secData->RegisterData(time, signal);
    } else {
      if(signal > threshold)
	secData->RegisterData(time, signal - fLoadPedestal);
    }
  }

  if(inFill) {
    secData->EndPad(fAutoPedestal, threshold);
    inFill = kFALSE;
  }
}
<|endoftext|>


<commit_before>/**
 * @Copyright 2015 seancode
 *
 * Handles display and saving of the settings dialog
 */

#include <QDir>
#include <QStandardPaths>
#include <QFileDialog>
#include <QSettings>
#include <QDebug>
#include "./settingsdialog.h"
#include "./ui_settingsdialog.h"
#include "./steamconfig.h"

SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent),
  ui(new Ui::SettingsDialog) {
  ui->setupUi(this);

  // autodetect paths

  SteamConfig steam;

  QString baseInstall = steam["software/valve/steam/baseinstallfolder_1"];

  QDir steamDir = QDir(baseInstall);
  // check if the path is empty before calling anything that acts on it
  // otherwise qdir complains
  if (baseInstall.isEmpty() || !steamDir.exists()) {
    steamDir.setPath(steam.getBase());
  }
  QString installDir = steam["software/valve/steam/apps/105600/installdir"];
  QDir terrariaDir = QDir(installDir);
  if (installDir.isEmpty() || !terrariaDir.exists())
    terrariaDir.setPath(steamDir.absoluteFilePath("SteamApps/common/Terraria"));

  defaultExes = "";
  defaultTextures = "";
  currentLanguage = "en-US";  // default for first start
  if (terrariaDir.exists()) {
#ifdef Q_OS_DARWIN
    // Darwin-based OS such as OS X and iOS, including any open source
    // version(s) of Darwin.
    defaultTextures = terrariaDir.absoluteFilePath("Terraria.app/Contents/Resources/Content/Images");
    defaultExes = terrariaDir.absoluteFilePath("Terraria.app/Contents/MacOS/Terraria.bin.osx");
#else
    defaultTextures = terrariaDir.absoluteFilePath("Content/Images");
    defaultExes = terrariaDir.absoluteFilePath("Terraria.exe");
#endif
  }

  QDir worldDir = QDir(
        QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation)
        .first());
  worldDir.setPath(worldDir.absoluteFilePath("My Games/Terraria/Worlds"));

  QStringList dataDirs = QStandardPaths::standardLocations(
              QStandardPaths::GenericDataLocation);
  // try linux paths
  for (const auto &dataDir : dataDirs) {
    if (worldDir.exists()) break;
    worldDir.setPath(dataDir);
    worldDir.setPath(worldDir.absoluteFilePath("Terraria/Worlds"));
  }

  QList<QDir> userDirs({ QDir(steamDir.absoluteFilePath("userdata")) });
  for (const auto &dataDir : dataDirs) {
      QDir userDir = QDir(dataDir).absoluteFilePath("Steam/userdata");
      if (userDir.exists()) {
          userDirs += userDir;
      }
  }

  QStringList steamWorldDirs;
  for (const auto &userDir : userDirs) {
    for (const auto &dir : userDir.entryInfoList(QDir::NoDotAndDotDot |
                                                 QDir::Dirs)) {
      QString steamWorldDir = QDir(dir.absoluteFilePath()).
          absoluteFilePath("105600/remote/worlds");
      if (QDir(steamWorldDir).exists())
        steamWorldDirs += steamWorldDir;
    }
  }

  defaultSaves = QStringList(worldDir.absolutePath()) + steamWorldDirs;

  QSettings info;
  useDefSave = info.value("useDefSave", true).toBool();
  customSave = info.value("customSave", defaultSaves[0]).toString();
  useDefTex = info.value("useDefTex", true).toBool();
  customTextures = info.value("customTextures", defaultTextures).toString();
  useDefExe = info.value("useDefExe", true).toBool();
  customExes = info.value("customExes", defaultExes).toString();
  currentLanguage = info.value("language", "").toString();
}

SettingsDialog::~SettingsDialog() {
  delete ui;
}

void SettingsDialog::setLanguages(QStringList l) {
  ui->languages->clear();
  ui->languages->addItems(l);
  ui->languages->setCurrentText(currentLanguage);
}

QString SettingsDialog::getLanguage() {
  return currentLanguage;
}

void SettingsDialog::show() {
  ui->defaultSavePath->setChecked(useDefSave);
  if (useDefSave)
    ui->savePath->setText(defaultSaves.join(",\n"));
  else
    ui->savePath->setText(customSave);
  ui->defaultTexturePath->setChecked(useDefTex);
  if (useDefTex)
    ui->texturePath->setText(defaultTextures);
  else
    ui->texturePath->setText(customTextures);
  ui->defaultExePath->setChecked(useDefExe);
  if (useDefExe) {
    ui->exePath->setText(defaultExes);
  } else {
    ui->exePath->setText(customExes);
  }

  ui->saveBrowse->setEnabled(!useDefSave);
  ui->savePath->setEnabled(!useDefSave);
  ui->textureBrowse->setEnabled(!useDefTex);
  ui->texturePath->setEnabled(!useDefTex);
  ui->exeBrowse->setEnabled(!useDefExe);
  ui->exePath->setEnabled(!useDefExe);
  QDialog::show();
}

void SettingsDialog::accept() {
  useDefSave = ui->defaultSavePath->isChecked();
  customSave = ui->savePath->text();
  useDefTex = ui->defaultTexturePath->isChecked();
  customTextures = ui->texturePath->text();
  useDefExe = ui->defaultExePath->isChecked();
  customExes = ui->exePath->text();
  currentLanguage = ui->languages->currentText();

  QSettings info;
  info.setValue("useDefSave", useDefSave);
  info.setValue("customSave", customSave);
  info.setValue("useDefTex", useDefTex);
  info.setValue("customTextures", customTextures);
  info.setValue("useDefExe", useDefExe);
  info.setValue("customExes", customExes);
  info.setValue("language", currentLanguage);
  QDialog::accept();
}

void SettingsDialog::toggleTextures(bool on) {
  ui->textureBrowse->setEnabled(!on);
  ui->texturePath->setEnabled(!on);
}

void SettingsDialog::toggleWorlds(bool on) {
  ui->saveBrowse->setEnabled(!on);
  ui->savePath->setEnabled(!on);
}

void SettingsDialog::toggleExes(bool on) {
  ui->exeBrowse->setEnabled(!on);
  ui->exePath->setEnabled(!on);
}

void SettingsDialog::browseTextures() {
  QString directory =
      QFileDialog::getExistingDirectory(this,
                                        tr("Find Texture Folder"),
                                        ui->texturePath->text());
  if (!directory.isEmpty())
    ui->texturePath->setText(directory);
}

void SettingsDialog::browseExes() {
  QString path = QFileDialog::getOpenFileName(this, tr("Find Terraria.exe"),
                                              ui->exePath->text(),
                                              "*.exe");
  if (!path.isEmpty()) {
    ui->exePath->setText(path);
  }
}

void SettingsDialog::browseWorlds() {
  QString directory =
      QFileDialog::getExistingDirectory(this,
                                        tr("Find World Folder"),
                                        ui->savePath->text());
  if (!directory.isEmpty())
    ui->savePath->setText(directory);
}

QString SettingsDialog::getTextures() {
  return useDefTex ? defaultTextures : customTextures;
}

QString SettingsDialog::getExe() {
  return useDefExe ? defaultExes : customExes;
}

QStringList SettingsDialog::getWorlds() {
  return useDefSave ? defaultSaves : QStringList(customSave);
}

QStringList SettingsDialog::getPlayers() {
  QStringList ret;
  for (const QString &worldDir : getWorlds()) {
    QDir dir(worldDir);
    dir.cdUp();
    if (!dir.cd("Players"))  // case-sensitive linux
      dir.cd("players");
    ret += dir.absolutePath();
  }
  return ret;
}
<commit_msg>Fix autodetect paths on Linux<commit_after>/**
 * @Copyright 2015 seancode
 *
 * Handles display and saving of the settings dialog
 */

#include <QDir>
#include <QStandardPaths>
#include <QFileDialog>
#include <QSettings>
#include <QDebug>
#include "./settingsdialog.h"
#include "./ui_settingsdialog.h"
#include "./steamconfig.h"

SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent),
  ui(new Ui::SettingsDialog) {
  ui->setupUi(this);

  // autodetect paths

  SteamConfig steam;

  QString baseInstall = steam["software/valve/steam/baseinstallfolder_1"];

  QDir steamDir = QDir(baseInstall);
  // check if the path is empty before calling anything that acts on it
  // otherwise qdir complains
  if (baseInstall.isEmpty() || !steamDir.exists()) {
    steamDir.setPath(steam.getBase());
  }
  QString installDir = steam["software/valve/steam/apps/105600/installdir"];
  QDir terrariaDir = QDir(installDir);
  if (installDir.isEmpty() || !terrariaDir.exists()) {
    terrariaDir.setPath(steamDir.absoluteFilePath("SteamApps/common/Terraria"));

    // On Linux the SteamApps directory is lower case
    if (!terrariaDir.exists())
      terrariaDir.setPath(steamDir.absoluteFilePath("steamapps/common/Terraria"));
  }

  defaultExes = "";
  defaultTextures = "";
  currentLanguage = "en-US";  // default for first start
  if (terrariaDir.exists()) {
#ifdef Q_OS_DARWIN
    // Darwin-based OS such as OS X and iOS, including any open source
    // version(s) of Darwin.
    defaultTextures = terrariaDir.absoluteFilePath("Terraria.app/Contents/Resources/Content/Images");
    defaultExes = terrariaDir.absoluteFilePath("Terraria.app/Contents/MacOS/Terraria.bin.osx");
#else
    defaultTextures = terrariaDir.absoluteFilePath("Content/Images");
    defaultExes = terrariaDir.absoluteFilePath("Terraria.exe");
#endif
  }

  QDir worldDir = QDir(
        QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation)
        .first());
  worldDir.setPath(worldDir.absoluteFilePath("My Games/Terraria/Worlds"));

  QStringList dataDirs = QStandardPaths::standardLocations(
              QStandardPaths::GenericDataLocation);
  // try linux paths
  for (const auto &dataDir : dataDirs) {
    if (worldDir.exists()) break;
    worldDir.setPath(dataDir);
    worldDir.setPath(worldDir.absoluteFilePath("Terraria/Worlds"));
  }

  QList<QDir> userDirs({ QDir(steamDir.absoluteFilePath("userdata")) });
  for (const auto &dataDir : dataDirs) {
      QDir userDir = QDir(dataDir).absoluteFilePath("Steam/userdata");
      if (userDir.exists()) {
          userDirs += userDir;
      }
  }

  QStringList steamWorldDirs;
  for (const auto &userDir : userDirs) {
    for (const auto &dir : userDir.entryInfoList(QDir::NoDotAndDotDot |
                                                 QDir::Dirs)) {
      QString steamWorldDir = QDir(dir.absoluteFilePath()).
          absoluteFilePath("105600/remote/worlds");
      if (QDir(steamWorldDir).exists())
        steamWorldDirs += steamWorldDir;
    }
  }

  defaultSaves = QStringList(worldDir.absolutePath()) + steamWorldDirs;

  QSettings info;
  useDefSave = info.value("useDefSave", true).toBool();
  customSave = info.value("customSave", defaultSaves[0]).toString();
  useDefTex = info.value("useDefTex", true).toBool();
  customTextures = info.value("customTextures", defaultTextures).toString();
  useDefExe = info.value("useDefExe", true).toBool();
  customExes = info.value("customExes", defaultExes).toString();
  currentLanguage = info.value("language", "").toString();
}

SettingsDialog::~SettingsDialog() {
  delete ui;
}

void SettingsDialog::setLanguages(QStringList l) {
  ui->languages->clear();
  ui->languages->addItems(l);
  ui->languages->setCurrentText(currentLanguage);
}

QString SettingsDialog::getLanguage() {
  return currentLanguage;
}

void SettingsDialog::show() {
  ui->defaultSavePath->setChecked(useDefSave);
  if (useDefSave)
    ui->savePath->setText(defaultSaves.join(",\n"));
  else
    ui->savePath->setText(customSave);
  ui->defaultTexturePath->setChecked(useDefTex);
  if (useDefTex)
    ui->texturePath->setText(defaultTextures);
  else
    ui->texturePath->setText(customTextures);
  ui->defaultExePath->setChecked(useDefExe);
  if (useDefExe) {
    ui->exePath->setText(defaultExes);
  } else {
    ui->exePath->setText(customExes);
  }

  ui->saveBrowse->setEnabled(!useDefSave);
  ui->savePath->setEnabled(!useDefSave);
  ui->textureBrowse->setEnabled(!useDefTex);
  ui->texturePath->setEnabled(!useDefTex);
  ui->exeBrowse->setEnabled(!useDefExe);
  ui->exePath->setEnabled(!useDefExe);
  QDialog::show();
}

void SettingsDialog::accept() {
  useDefSave = ui->defaultSavePath->isChecked();
  customSave = ui->savePath->text();
  useDefTex = ui->defaultTexturePath->isChecked();
  customTextures = ui->texturePath->text();
  useDefExe = ui->defaultExePath->isChecked();
  customExes = ui->exePath->text();
  currentLanguage = ui->languages->currentText();

  QSettings info;
  info.setValue("useDefSave", useDefSave);
  info.setValue("customSave", customSave);
  info.setValue("useDefTex", useDefTex);
  info.setValue("customTextures", customTextures);
  info.setValue("useDefExe", useDefExe);
  info.setValue("customExes", customExes);
  info.setValue("language", currentLanguage);
  QDialog::accept();
}

void SettingsDialog::toggleTextures(bool on) {
  ui->textureBrowse->setEnabled(!on);
  ui->texturePath->setEnabled(!on);
}

void SettingsDialog::toggleWorlds(bool on) {
  ui->saveBrowse->setEnabled(!on);
  ui->savePath->setEnabled(!on);
}

void SettingsDialog::toggleExes(bool on) {
  ui->exeBrowse->setEnabled(!on);
  ui->exePath->setEnabled(!on);
}

void SettingsDialog::browseTextures() {
  QString directory =
      QFileDialog::getExistingDirectory(this,
                                        tr("Find Texture Folder"),
                                        ui->texturePath->text());
  if (!directory.isEmpty())
    ui->texturePath->setText(directory);
}

void SettingsDialog::browseExes() {
  QString path = QFileDialog::getOpenFileName(this, tr("Find Terraria.exe"),
                                              ui->exePath->text(),
                                              "*.exe");
  if (!path.isEmpty()) {
    ui->exePath->setText(path);
  }
}

void SettingsDialog::browseWorlds() {
  QString directory =
      QFileDialog::getExistingDirectory(this,
                                        tr("Find World Folder"),
                                        ui->savePath->text());
  if (!directory.isEmpty())
    ui->savePath->setText(directory);
}

QString SettingsDialog::getTextures() {
  return useDefTex ? defaultTextures : customTextures;
}

QString SettingsDialog::getExe() {
  return useDefExe ? defaultExes : customExes;
}

QStringList SettingsDialog::getWorlds() {
  return useDefSave ? defaultSaves : QStringList(customSave);
}

QStringList SettingsDialog::getPlayers() {
  QStringList ret;
  for (const QString &worldDir : getWorlds()) {
    QDir dir(worldDir);
    dir.cdUp();
    if (!dir.cd("Players"))  // case-sensitive linux
      dir.cd("players");
    ret += dir.absolutePath();
  }
  return ret;
}
<|endoftext|>


<commit_before>/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "Timeval.h"

using namespace std;

void Timeval::future(unsigned offset)
{
	now();
	unsigned sec = offset/1000;
	unsigned msec = offset%1000;
	mTimeval.tv_usec += msec*1000;
	mTimeval.tv_sec += sec;
	if (mTimeval.tv_usec>1000000) {
		mTimeval.tv_usec -= 1000000;
		mTimeval.tv_sec += 1;
	}
}

struct timespec Timeval::timespec() const
{
	struct timespec retVal;
	retVal.tv_sec = mTimeval.tv_sec;
	retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
	return retVal;
}

bool Timeval::passed() const
{
	Timeval nowTime;
	if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
	if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
	if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true;
	return false;
}

double Timeval::seconds() const
{
	return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
}

long Timeval::delta(const Timeval& other) const
{
	// 2^31 milliseconds is just over 4 years.
	int32_t deltaS = other.sec() - sec();
	int32_t deltaUs = other.usec() - usec();
	return 1000*deltaS + deltaUs/1000;
}

ostream& operator<<(ostream& os, const Timeval& tv)
{
	os.setf( ios::fixed, ios::floatfield );
	os << tv.seconds();
	return os;
}

ostream& operator<<(ostream& os, const struct timespec& ts)
{
	os << ts.tv_sec << "," << ts.tv_nsec;
	return os;
}

// vim: ts=4 sw=4
<commit_msg>Timeval: passed() returns true if time is equal<commit_after>/*
* Copyright 2008 Free Software Foundation, Inc.
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "Timeval.h"

using namespace std;

void Timeval::future(unsigned offset)
{
	now();
	unsigned sec = offset/1000;
	unsigned msec = offset%1000;
	mTimeval.tv_usec += msec*1000;
	mTimeval.tv_sec += sec;
	if (mTimeval.tv_usec>1000000) {
		mTimeval.tv_usec -= 1000000;
		mTimeval.tv_sec += 1;
	}
}

struct timespec Timeval::timespec() const
{
	struct timespec retVal;
	retVal.tv_sec = mTimeval.tv_sec;
	retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec;
	return retVal;
}

bool Timeval::passed() const
{
	Timeval nowTime;
	if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false;
	if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true;
	if (nowTime.mTimeval.tv_usec >= mTimeval.tv_usec) return true;
	return false;
}

double Timeval::seconds() const
{
	return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec);
}

long Timeval::delta(const Timeval& other) const
{
	// 2^31 milliseconds is just over 4 years.
	int32_t deltaS = other.sec() - sec();
	int32_t deltaUs = other.usec() - usec();
	return 1000*deltaS + deltaUs/1000;
}

ostream& operator<<(ostream& os, const Timeval& tv)
{
	os.setf( ios::fixed, ios::floatfield );
	os << tv.seconds();
	return os;
}

ostream& operator<<(ostream& os, const struct timespec& ts)
{
	os << ts.tv_sec << "," << ts.tv_nsec;
	return os;
}

// vim: ts=4 sw=4
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "replica.h"
#include "mutation.h"
#include "mutation_log.h"
#include "replica_stub.h"
#include <boost/filesystem.hpp>

# ifdef __TITLE__
# undef __TITLE__
# endif
# define __TITLE__ "replica.learn"

namespace dsn { namespace replication {

void replica::init_learn(uint64_t signature)
{
    check_hashed_access();

    dassert (status() == PS_POTENTIAL_SECONDARY, "");

    // at most one learning task running
    if (_potential_secondary_states.learning_round_is_running || !signature)
        return;

    if (signature != _potential_secondary_states.learning_signature)
    {
        _potential_secondary_states.cleanup(true);
        _potential_secondary_states.learning_signature = signature;
        _potential_secondary_states.learning_status = LearningWithoutPrepare;
        _prepare_list->reset(_app->last_committed_decree());
    }
    else
    {
        switch (_potential_secondary_states.learning_status)
        {
        case LearningSucceeded:
            notify_learn_completion();
            return;
        case LearningFailed:
            break;
        case LearningWithPrepare:
            if (_app->last_durable_decree() >= last_committed_decree())
            {
                _potential_secondary_states.learning_status = LearningSucceeded;
                notify_learn_completion();
                return;
            }
            break;
        case LearningWithoutPrepare:
            break;
        default:
            dassert (false, "");
        }
    }

    _potential_secondary_states.learning_round_is_running = true;

    std::shared_ptr<learn_request> request(new learn_request);
    request->gpid = get_gpid();
    request->last_committed_decree_in_app = _app->last_committed_decree();
    request->last_committed_decree_in_prepare_list = _prepare_list->last_committed_decree();
    request->learner = primary_address();
    request->signature = _potential_secondary_states.learning_signature;
    _app->prepare_learning_request(request->app_specific_learn_request);

    _potential_secondary_states.learning_task = rpc::call_typed(
        _config.primary,
        RPC_LEARN,
        request,
        this,
        &replica::on_learn_reply,
        gpid_to_hash(get_gpid())
        );

    ddebug(
        "%s: init_learn with lastAppC/DDecree = <%llu,%llu>, lastCDecree = %llu, learnState = %s",
        name(),
        _app->last_committed_decree(),
        _app->last_durable_decree(),
        last_committed_decree(),
        enum_to_string(_potential_secondary_states.learning_status)
        );
}

void replica::on_learn(const learn_request& request, __out_param learn_response& response)
{
    check_hashed_access();

    if (PS_PRIMARY  != status())
    {
        response.err = ERR_INVALID_STATE;
        return;
    }

    _primary_states.get_replica_config(request.learner, response.config);

    auto it = _primary_states.learners.find(request.learner);
    if (it == _primary_states.learners.end())
    {
        response.err = (response.config.status == PS_SECONDARY ? ERR_OK : ERR_OBJECT_NOT_FOUND);
        return;
    }
    else if (it->second.signature != request.signature)
    {
        response.err = ERR_OBJECT_NOT_FOUND;
        return;
    }

    // prepare learnStartDecree
    decree localCommittedDecree = last_committed_decree();
    decree learnStartDecree = request.last_committed_decree_in_app + 1;
    if (request.last_committed_decree_in_app > localCommittedDecree)
    {
        ddebug(
            "%s: on_learn %s:%d, learner state is lost due to DDD, "
            "with its appCommittedDecree = %llu vs localCommittedDecree = %llu, "
            "so we learn from scratch by setting learnStartDecree = 0",
            name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
            request.last_committed_decree_in_app, localCommittedDecree
            );
        learnStartDecree = 0; // 0 means learn from scratch
    }

    ddebug(
        "%s: on_learn %s:%d, with localCommittedDecree = %llu and learnStartDecree = %llu",
        name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
        localCommittedDecree, learnStartDecree
        );

    response.prepare_start_decree = invalid_decree;
    response.commit_decree = localCommittedDecree;
    response.err = ERR_OK;

    // set prepare_start_decree if need
    if (learnStartDecree + _options.staleness_for_start_prepare_for_potential_secondary > localCommittedDecree)
    {
        if (it->second.prepare_start_decree == invalid_decree)
        {
            // start from (last_committed_decree + 1)
            it->second.prepare_start_decree = localCommittedDecree + 1;

            // TODO(qinzuoyan) replay before response on_learn, learner may reject prepare request?
            cleanup_preparing_mutations(true);
            replay_prepare_list();

            ddebug(
                "%s: on_learn %s:%d, set prepareStartDecree = %llu",
                name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
                localCommittedDecree + 1
            );
        }

        response.prepare_start_decree = it->second.prepare_start_decree;
    }
    else
    {
        it->second.prepare_start_decree = invalid_decree;
    }

    int lerr = _app->get_learn_state(learnStartDecree, request.app_specific_learn_request, response.state);
    if (lerr != 0)
    {
        response.err = ERR_GET_LEARN_STATE_FALED;
        derror("%s get learn state failed, error = %d", dir().c_str(), lerr);
    }
    else
    {
        response.base_local_dir = _app->data_dir();
        for (auto itr = response.state.files.begin(); itr != response.state.files.end(); ++itr)
            *itr = itr->substr(_app->data_dir().length());
    }
}

void replica::on_learn_reply(error_code err, std::shared_ptr<learn_request>& req, std::shared_ptr<learn_response>& resp)
{
    check_hashed_access();

    dassert(PS_POTENTIAL_SECONDARY == status(), "");
    dassert(req->signature == _potential_secondary_states.learning_signature, "");

    if (err != ERR_OK)
    {
        handle_learning_error(err);
        return;
    }

    ddebug(
        "%s: on_learn_reply with err = %s, prepare_start_decree = %llu, current learnState = %s",
        name(), resp->err.to_string(), resp->prepare_start_decree, enum_to_string(_potential_secondary_states.learning_status)
        );

    if (resp->err != ERR_OK)
    {
        handle_learning_error(resp->err);
        return;
    }

    if (resp->config.ballot > get_ballot())
    {
        update_local_configuration(resp->config);
    }

    if (status() != PS_POTENTIAL_SECONDARY)
    {
        return;
    }

    if (resp->prepare_start_decree != invalid_decree && _potential_secondary_states.learning_status == LearningWithoutPrepare)
    {
        _potential_secondary_states.learning_status = LearningWithPrepare;
        _prepare_list->reset(resp->prepare_start_decree - 1);
    }

    if (resp->state.files.size() > 0)
    {
        _potential_secondary_states.learn_remote_files_task =
            file::copy_remote_files(resp->config.primary,
                resp->base_local_dir,
                resp->state.files,
                _app->learn_dir(),
                true,
                LPC_COPY_REMOTE_DELTA_FILES,
                this,
                std::bind(&replica::on_copy_remote_state_completed, this,
                std::placeholders::_1,
                std::placeholders::_2,
                resp)
                );
    }
    else
    {
        _potential_secondary_states.learn_remote_files_task = tasking::enqueue(
            LPC_LEARN_REMOTE_DELTA_FILES,
            this,
            std::bind(&replica::on_copy_remote_state_completed, this, ERR_OK, 0, resp)
            );
    }
}

void replica::on_copy_remote_state_completed(error_code err2, int size, std::shared_ptr<learn_response> resp)
{
    learn_state localState;
    localState.meta = resp->state.meta;
    if (err2 == ERR_OK)
    {
        for (auto itr = resp->state.files.begin(); itr != resp->state.files.end(); ++itr)
        {
            std::string file;
            if (dir().back() == '/' || itr->front() == '/')
                file = dir() + *itr;
            else
                file = dir() + '/' + *itr;

            localState.files.push_back(file);
        }

        decree oldDecree = _app->last_committed_decree();

        // the only place where there is non-in-partition-thread update
        int err = _app->apply_learn_state(resp->state);
        if (err == 0)
        {
            dassert(_app->last_committed_decree() >= _app->last_durable_decree(), "");
            // because if the original _app->last_committed_decree > resp->commit_decree,
            // the learnStartDecree will be set to 0, which makes learner to learn from scratch
            dassert(_app->last_committed_decree() <= resp->commit_decree, "");
        }

        ddebug(
                "%s: learning %d files to %s, err = %x, "
                "appCommit(%llu => %llu), durable(%llu), "
                "remoteC(%llu), prepStart(%llu), state(%s)",
                name(),
                resp->state.files.size(), _dir.c_str(), err,
                oldDecree, _app->last_committed_decree(),
                _app->last_durable_decree(),
                resp->commit_decree,
                resp->prepare_start_decree,
                enum_to_string(_potential_secondary_states.learning_status)
              );

        // if catch-up done, do flush to force data in memtables to the ground
        if (err == 0 && _app->last_committed_decree() == resp->commit_decree)
        {
            err = _app->flush(true);
            if (err == 0)
            {
                dassert (_app->last_committed_decree() == _app->last_durable_decree(), "");
            }
        }

        // translate to general error code
        if (err != 0)
        {
            err2 = ERR_LOCAL_APP_FAILURE;
        }
    }
    else
    {
        derror(
                "%s: transfer %d files to %s failed, err = %s",
                name(),
                static_cast<int>(resp->state.files.size()),
                _dir.c_str(),
                err2.to_string()
                );
    }

    _potential_secondary_states.learn_remote_files_completed_task = tasking::enqueue(
        LPC_LEARN_REMOTE_DELTA_FILES_COMPLETED,
        this,
        std::bind(&replica::on_learn_remote_state_completed, this, err2),
        gpid_to_hash(get_gpid())
        );
}

void replica::on_learn_remote_state_completed(error_code err)
{
    check_hashed_access();

    if (PS_POTENTIAL_SECONDARY != status())
        return;

    _potential_secondary_states.learning_round_is_running = false;

    if (err != ERR_OK)
    {
        handle_learning_error(err);
    }
    else
    {
        // continue
        init_learn(_potential_secondary_states.learning_signature);
    }
}

void replica::handle_learning_error(error_code err)
{
    check_hashed_access();

    dwarn(
        "%s: learning failed with err = %s, LastCommitted = %lld",
        name(),
        err.to_string(),
        _app->last_committed_decree()
        );

    _potential_secondary_states.cleanup(true);
    _potential_secondary_states.learning_status = LearningFailed;

    update_local_configuration_with_no_ballot_change(PS_ERROR);
}

void replica::handle_learning_succeeded_on_primary(const end_point& node, uint64_t learnSignature)
{
    auto it = _primary_states.learners.find(node);
    if (it != _primary_states.learners.end() && it->second.signature == learnSignature)
        upgrade_to_secondary_on_primary(node);
}

void replica::notify_learn_completion()
{
    group_check_response report;
    report.gpid = get_gpid();
    report.err = ERR_OK;
    report.last_committed_decree_in_app = _app->last_committed_decree();
    report.last_committed_decree_in_prepare_list = last_committed_decree();
    report.learner_signature = _potential_secondary_states.learning_signature;
    report.learner_status_ = _potential_secondary_states.learning_status;
    report.node = primary_address();

    rpc::call_one_way_typed(_config.primary, RPC_LEARN_COMPLETION_NOTIFY, report, gpid_to_hash(get_gpid()));
}

void replica::on_learn_completion_notification(const group_check_response& report)
{
    check_hashed_access();
    report.err.end_tracking();
    if (status() != PS_PRIMARY)
        return;

    if (report.learner_status_ == LearningSucceeded)
    {
        handle_learning_succeeded_on_primary(report.node, report.learner_signature);
    }
}

void replica::on_add_learner(const group_check_request& request)
{
    if (request.config.ballot < get_ballot())
        return;

    if (request.config.ballot > get_ballot()
        || is_same_ballot_status_change_allowed(status(), request.config.status))
    {
        update_local_configuration(request.config, true);
        dassert(PS_POTENTIAL_SECONDARY == status(), "");
        init_learn(request.learner_signature);
    }
}

}} // namespace
<commit_msg>delete js part<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "replica.h"
#include "mutation.h"
#include "mutation_log.h"
#include "replica_stub.h"
#include <boost/filesystem.hpp>

# ifdef __TITLE__
# undef __TITLE__
# endif
# define __TITLE__ "replica.learn"

namespace dsn {
    namespace replication {

        void replica::init_learn(uint64_t signature)
        {
            check_hashed_access();

            dassert(status() == PS_POTENTIAL_SECONDARY, "");

            // at most one learning task running
            if (_potential_secondary_states.learning_round_is_running || !signature)
                return;

            if (signature != _potential_secondary_states.learning_signature)
            {
                _potential_secondary_states.cleanup(true);
                _potential_secondary_states.learning_signature = signature;
                _potential_secondary_states.learning_status = LearningWithoutPrepare;
                _prepare_list->reset(_app->last_committed_decree());
            }
            else
            {
                switch (_potential_secondary_states.learning_status)
                {
                case LearningSucceeded:
                    notify_learn_completion();
                    return;
                case LearningFailed:
                    break;
                case LearningWithPrepare:
                    if (_app->last_durable_decree() >= last_committed_decree())
                    {
                        _potential_secondary_states.learning_status = LearningSucceeded;
                        notify_learn_completion();
                        return;
                    }
                    break;
                case LearningWithoutPrepare:
                    break;
                default:
                    dassert(false, "");
                }
            }

            _potential_secondary_states.learning_round_is_running = true;

            std::shared_ptr<learn_request> request(new learn_request);
            request->gpid = get_gpid();
            request->last_committed_decree_in_app = _app->last_committed_decree();
            request->last_committed_decree_in_prepare_list = _prepare_list->last_committed_decree();
            request->learner = primary_address();
            request->signature = _potential_secondary_states.learning_signature;
            _app->prepare_learning_request(request->app_specific_learn_request);

            _potential_secondary_states.learning_task = rpc::call_typed(
                _config.primary,
                RPC_LEARN,
                request,
                this,
                &replica::on_learn_reply,
                gpid_to_hash(get_gpid())
                );

            ddebug(
                "%s: init_learn with lastAppC/DDecree = <%llu,%llu>, lastCDecree = %llu, learnState = %s",
                name(),
                _app->last_committed_decree(),
                _app->last_durable_decree(),
                last_committed_decree(),
                enum_to_string(_potential_secondary_states.learning_status)
                );
        }

        void replica::on_learn(const learn_request& request, __out_param learn_response& response)
        {
            check_hashed_access();

            if (PS_PRIMARY != status())
            {
                response.err = ERR_INVALID_STATE;
                return;
            }

            _primary_states.get_replica_config(request.learner, response.config);

            auto it = _primary_states.learners.find(request.learner);
            if (it == _primary_states.learners.end())
            {
                response.err = (response.config.status == PS_SECONDARY ? ERR_OK : ERR_OBJECT_NOT_FOUND);
                return;
            }
            else if (it->second.signature != request.signature)
            {
                response.err = ERR_OBJECT_NOT_FOUND;
                return;
            }

            // prepare learnStartDecree
            decree localCommittedDecree = last_committed_decree();
            decree learnStartDecree = request.last_committed_decree_in_app + 1;
            if (request.last_committed_decree_in_app > localCommittedDecree)
            {
                ddebug(
                    "%s: on_learn %s:%d, learner state is lost due to DDD, "
                    "with its appCommittedDecree = %llu vs localCommittedDecree = %llu, "
                    "so we learn from scratch by setting learnStartDecree = 0",
                    name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
                    request.last_committed_decree_in_app, localCommittedDecree
                    );
                learnStartDecree = 0; // 0 means learn from scratch
            }

            ddebug(
                "%s: on_learn %s:%d, with localCommittedDecree = %llu and learnStartDecree = %llu",
                name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
                localCommittedDecree, learnStartDecree
                );

            response.prepare_start_decree = invalid_decree;
            response.commit_decree = localCommittedDecree;
            response.err = ERR_OK;

            // set prepare_start_decree if need
            if (learnStartDecree + _options.staleness_for_start_prepare_for_potential_secondary > localCommittedDecree)
            {
                if (it->second.prepare_start_decree == invalid_decree)
                {
                    // start from (last_committed_decree + 1)
                    it->second.prepare_start_decree = localCommittedDecree + 1;

                    // TODO(qinzuoyan) replay before response on_learn, learner may reject prepare request?
                    cleanup_preparing_mutations(true);
                    replay_prepare_list();

                    ddebug(
                        "%s: on_learn %s:%d, set prepareStartDecree = %llu",
                        name(), request.learner.name.c_str(), static_cast<int>(request.learner.port),
                        localCommittedDecree + 1
                        );
                }

                response.prepare_start_decree = it->second.prepare_start_decree;
            }
            else
            {
                it->second.prepare_start_decree = invalid_decree;
            }

            int lerr = _app->get_learn_state(learnStartDecree, request.app_specific_learn_request, response.state);
            if (lerr != 0)
            {
                response.err = ERR_GET_LEARN_STATE_FALED;
                derror("%s get learn state failed, error = %d", dir().c_str(), lerr);
            }
            else
            {
                response.base_local_dir = _app->data_dir();
                for (auto itr = response.state.files.begin(); itr != response.state.files.end(); ++itr)
                    *itr = itr->substr(_app->data_dir().length());
            }
        }

        void replica::on_learn_reply(error_code err, std::shared_ptr<learn_request>& req, std::shared_ptr<learn_response>& resp)
        {
            check_hashed_access();

            dassert(PS_POTENTIAL_SECONDARY == status(), "");
            dassert(req->signature == _potential_secondary_states.learning_signature, "");

            if (err != ERR_OK)
            {
                handle_learning_error(err);
                return;
            }

            ddebug(
                "%s: on_learn_reply with err = %s, prepare_start_decree = %llu, current learnState = %s",
                name(), resp->err.to_string(), resp->prepare_start_decree, enum_to_string(_potential_secondary_states.learning_status)
                );

            if (resp->err != ERR_OK)
            {
                handle_learning_error(resp->err);
                return;
            }

            if (resp->config.ballot > get_ballot())
            {
                update_local_configuration(resp->config);
            }

            if (status() != PS_POTENTIAL_SECONDARY)
            {
                return;
            }

            if (resp->prepare_start_decree != invalid_decree && _potential_secondary_states.learning_status == LearningWithoutPrepare)
            {
                _potential_secondary_states.learning_status = LearningWithPrepare;
                _prepare_list->reset(resp->prepare_start_decree - 1);
            }

            if (resp->state.files.size() > 0)
            {
                _potential_secondary_states.learn_remote_files_task =
                    file::copy_remote_files(resp->config.primary,
                    resp->base_local_dir,
                    resp->state.files,
                    _app->learn_dir(),
                    true,
                    LPC_COPY_REMOTE_DELTA_FILES,
                    this,
                    std::bind(&replica::on_copy_remote_state_completed, this,
                    std::placeholders::_1,
                    std::placeholders::_2,
                    resp)
                    );
            }
            else
            {
                _potential_secondary_states.learn_remote_files_task = tasking::enqueue(
                    LPC_LEARN_REMOTE_DELTA_FILES,
                    this,
                    std::bind(&replica::on_copy_remote_state_completed, this, ERR_OK, 0, resp)
                    );
            }
        }

        void replica::on_copy_remote_state_completed(error_code err2, int size, std::shared_ptr<learn_response> resp)
        {
            learn_state localState;
            localState.meta = resp->state.meta;
            if (err2 == ERR_OK)
            {
                for (auto itr = resp->state.files.begin(); itr != resp->state.files.end(); ++itr)
                {
                    std::string file;
                    if (dir().back() == '/' || itr->front() == '/')
                        file = dir() + *itr;
                    else
                        file = dir() + '/' + *itr;

                    localState.files.push_back(file);
                }

                decree oldDecree = _app->last_committed_decree();

                // the only place where there is non-in-partition-thread update
                int err = _app->apply_learn_state(resp->state);
                if (err == 0)
                {
                    dassert(_app->last_committed_decree() >= _app->last_durable_decree(), "");
                    // because if the original _app->last_committed_decree > resp->commit_decree,
                    // the learnStartDecree will be set to 0, which makes learner to learn from scratch
                    dassert(_app->last_committed_decree() <= resp->commit_decree, "");
                }

                ddebug(
                    "%s: learning %d files to %s, err = %x, "
                    "appCommit(%llu => %llu), durable(%llu), "
                    "remoteC(%llu), prepStart(%llu), state(%s)",
                    name(),
                    resp->state.files.size(), _dir.c_str(), err,
                    oldDecree, _app->last_committed_decree(),
                    _app->last_durable_decree(),
                    resp->commit_decree,
                    resp->prepare_start_decree,
                    enum_to_string(_potential_secondary_states.learning_status)
                    );

                // if catch-up done, do flush to force data in memtables to the ground
                if (err == 0 && _app->last_committed_decree() == resp->commit_decree)
                {
                    err = _app->flush(true);
                    if (err == 0)
                    {
                        dassert(_app->last_committed_decree() == _app->last_durable_decree(), "");
                    }
                }

                // translate to general error code
                if (err != 0)
                {
                    err2 = ERR_LOCAL_APP_FAILURE;
                }
            }
            else
            {
                derror(
                    "%s: transfer %d files to %s failed, err = %s",
                    name(),
                    static_cast<int>(resp->state.files.size()),
                    _dir.c_str(),
                    err2.to_string()
                    );
            }

            _potential_secondary_states.learn_remote_files_completed_task = tasking::enqueue(
                LPC_LEARN_REMOTE_DELTA_FILES_COMPLETED,
                this,
                std::bind(&replica::on_learn_remote_state_completed, this, err2),
                gpid_to_hash(get_gpid())
                );
        }

        void replica::on_learn_remote_state_completed(error_code err)
        {
            check_hashed_access();

            if (PS_POTENTIAL_SECONDARY != status())
                return;

            _potential_secondary_states.learning_round_is_running = false;

            if (err != ERR_OK)
            {
                handle_learning_error(err);
            }
            else
            {
                // continue
                init_learn(_potential_secondary_states.learning_signature);
            }
        }

        void replica::handle_learning_error(error_code err)
        {
            check_hashed_access();

            dwarn(
                "%s: learning failed with err = %s, LastCommitted = %lld",
                name(),
                err.to_string(),
                _app->last_committed_decree()
                );

            _potential_secondary_states.cleanup(true);
            _potential_secondary_states.learning_status = LearningFailed;

            update_local_configuration_with_no_ballot_change(PS_ERROR);
        }

        void replica::handle_learning_succeeded_on_primary(const end_point& node, uint64_t learnSignature)
        {
            auto it = _primary_states.learners.find(node);
            if (it != _primary_states.learners.end() && it->second.signature == learnSignature)
                upgrade_to_secondary_on_primary(node);
        }

        void replica::notify_learn_completion()
        {
            group_check_response report;
            report.gpid = get_gpid();
            report.err = ERR_OK;
            report.last_committed_decree_in_app = _app->last_committed_decree();
            report.last_committed_decree_in_prepare_list = last_committed_decree();
            report.learner_signature = _potential_secondary_states.learning_signature;
            report.learner_status_ = _potential_secondary_states.learning_status;
            report.node = primary_address();

            rpc::call_one_way_typed(_config.primary, RPC_LEARN_COMPLETION_NOTIFY, report, gpid_to_hash(get_gpid()));
        }

        void replica::on_learn_completion_notification(const group_check_response& report)
        {
            check_hashed_access();
            report.err.end_tracking();
            if (status() != PS_PRIMARY)
                return;

            if (report.learner_status_ == LearningSucceeded)
            {
                handle_learning_succeeded_on_primary(report.node, report.learner_signature);
            }
        }

        void replica::on_add_learner(const group_check_request& request)
        {
            if (request.config.ballot < get_ballot())
                return;

            if (request.config.ballot > get_ballot()
                || is_same_ballot_status_change_allowed(status(), request.config.status))
            {
                update_local_configuration(request.config, true);
                dassert(PS_POTENTIAL_SECONDARY == status(), "");
                init_learn(request.learner_signature);
            }
        }

    }
} // namespace<|endoftext|>