<commit_before>/* Copyright 2016 The TensorFlow Authors. 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.
==============================================================================*/

#include "tensorflow/core/distributed_runtime/tensor_coding.h"

#include "google/protobuf/any.pb.h"

#include "tensorflow/core/common_runtime/device.h"
#include "tensorflow/core/framework/tensor.pb.h"
#include "tensorflow/core/framework/tensor_shape.pb.h"

namespace tensorflow {

TensorResponse::Source::~Source() {}

void TensorResponse::Clear() {
  on_host_ = false;
  device_ = nullptr;
  alloc_attrs_ = AllocatorAttributes();
  allocator_ = nullptr;
  already_used_ = false;
  ClearTensor();
}

void TensorResponse::ClearTensor() {
  meta_.Clear();
  tensor_ = Tensor();
}

void TensorResponse::InitAlloc(DeviceBase* d, const AllocatorAttributes& aa) {
  Clear();
  device_ = d;
  alloc_attrs_ = aa;
  const DeviceAttributes& da = d->attributes();
  if (alloc_attrs_.on_host() || da.device_type() == "CPU") {
    on_host_ = true;
  }
  allocator_ = device_->GetAllocator(alloc_attrs_);
}

Status TensorResponse::InitFrom(RecvTensorResponse* response) {
  Status s;
  meta_.Swap(response);
  if (on_host_) {
    if (!tensor_.FromProto(allocator_, meta_.tensor())) {
      s = errors::InvalidArgument("Cannot parse tensor from response");
    }
  } else {
    s = device_->MakeTensorFromProto(meta_.tensor(), alloc_attrs_, &tensor_);
  }
  {
    TensorProto empty;
    meta_.mutable_tensor()->Swap(&empty);
  }
  meta_.clear_tensor();
  return s;
}

void TensorResponse::InitPartial(const RecvTensorResponse& response) {
  // Everything except content is present in *response.  Content will
  // arrive later; allocate a Tensor with appropriate storage for that
  // content.
  meta_ = response;
  TensorShape shape(meta_.tensor().tensor_shape());
  Tensor t(allocator_, meta_.tensor().dtype(), shape);
  tensor_ = std::move(t);
}

Status TensorResponse::ParseFrom(Source* source) {
  if (!on_host_) {
    protobuf::io::CodedInputStream input(source->contents());
    input.SetTotalBytesLimit(INT_MAX);  // Unlimited

    // Pre-parse into local storage, then delegate to device.
    if (!meta_.ParseFromCodedStream(&input) || !input.ConsumedEntireMessage()) {
      return errors::InvalidArgument("Cannot parse tensor from response");
    }
    Status s =
        device_->MakeTensorFromProto(meta_.tensor(), alloc_attrs_, &tensor_);
    // Reduce memory usage for big tensors.
    {
      TensorProto empty;
      meta_.mutable_tensor()->Swap(&empty);
    }
    meta_.clear_tensor();
    return s;
  }
  if (already_used_) {
    ClearTensor();
  }
  already_used_ = true;
  if (ParseFast(source)) return Status::OK();
  meta_.Clear();
  if (ParseSlow(source)) return Status::OK();
  return errors::InvalidArgument("Cannot parse tensor from response");
}

// Define some helper routines for decoding protocol buffer wire format data
namespace {
// We only need some of the wiretype values for this code
enum WireType {
  WIRETYPE_VARINT = 0,
  WIRETYPE_LENGTH_DELIMITED = 2,
};
inline int GetTagFieldNumber(uint32 tag) { return tag >> 3; }
inline WireType GetTagWireType(uint32 tag) {
  return static_cast<WireType>(tag & 0x7);
}

bool ReadVarintSizeAsInt(protobuf::io::CodedInputStream* input, int* result) {
  protobuf_uint64 v;
  if (input->ReadVarint64(&v) && v <= static_cast<uint64>(INT_MAX)) {
    *result = static_cast<int>(v);
    return true;
  } else {
    return false;
  }
}

bool ReadNestedMessage(protobuf::io::CodedInputStream* input,
                       protobuf::Message* value) {
  int length;
  if (!ReadVarintSizeAsInt(input, &length)) return false;
  std::pair<protobuf::io::CodedInputStream::Limit, int> p =
      input->IncrementRecursionDepthAndPushLimit(length);
  if (p.second < 0 || !value->MergePartialFromCodedStream(input)) return false;
  // Make sure that parsing stopped when the limit was hit, not at an endgroup
  // tag.
  return input->DecrementRecursionDepthAndPopLimit(p.first);
}

}  // namespace

bool TensorResponse::ParseTensorSubmessage(
    protobuf::io::CodedInputStream* input, TensorProto* tensor_meta) {
  bool seen_tensor_content = false;
  while (true) {
    auto p = input->ReadTagWithCutoff(127);
    int tag = GetTagFieldNumber(p.first);
    WireType wt = GetTagWireType(p.first);
    if (!p.second) {
      bool ok = (tag == 0);
      if (ok && !seen_tensor_content) {
        // No tensor content: could be because it's a zero-length tensor
        TensorShape shape(tensor_meta->tensor_shape());
        Tensor t(allocator_, tensor_meta->dtype(), shape);
        tensor_ = std::move(t);
      }
      return ok;
    }
    switch (tag) {
      case TensorProto::kDtypeFieldNumber: {
        uint32 v;
        if ((wt != WIRETYPE_VARINT) || !input->ReadVarint32(&v)) return false;
        if (seen_tensor_content) return false;
        tensor_meta->set_dtype(static_cast<DataType>(static_cast<int>(v)));
        if (!DataTypeCanUseMemcpy(tensor_meta->dtype())) return false;
        break;
      }
      case TensorProto::kTensorShapeFieldNumber: {
        if ((wt != WIRETYPE_LENGTH_DELIMITED) ||
            !ReadNestedMessage(input, tensor_meta->mutable_tensor_shape()))
          return false;
        if (seen_tensor_content) return false;
        break;
      }
      case TensorProto::kVersionNumberFieldNumber: {
        uint32 v;
        if ((wt != WIRETYPE_VARINT) || !input->ReadVarint32(&v)) return false;
        if (seen_tensor_content) return false;
        tensor_meta->set_version_number(static_cast<int32>(v));
        break;
      }
      case TensorProto::kTensorContentFieldNumber: {
        // If we haven't seen the dtype and tensor_shape data first, we can't
        // deal with this in the fast path.
        if (seen_tensor_content) return false;
        if (wt != WIRETYPE_LENGTH_DELIMITED ||
            !tensor_meta->has_tensor_shape()) {
          return false;
        }
        int num_bytes;
        if (!ReadVarintSizeAsInt(input, &num_bytes)) return false;
        seen_tensor_content = true;
        TensorShape shape(tensor_meta->tensor_shape());
        Tensor t(allocator_, tensor_meta->dtype(), shape);
        StringPiece buf = t.tensor_data();
        if (static_cast<size_t>(num_bytes) != buf.size()) return false;
        // TODO(jeff,sanjay): Figure out a way to avoid this copy if
        // the underlying ZeroCopyInputStream data is properly aligned
        // and compatible with what allocator_ wants.
        if (!input->ReadRaw(const_cast<char*>(buf.data()), num_bytes))
          return false;
        tensor_ = std::move(t);
        break;
      }
      default: {
        // Some other tag our fast path code is not prepared to handle.
        // return false.
        return false;
      }
    }
  }
}

bool TensorResponse::ParseFast(Source* source) {
  protobuf::io::CodedInputStream input(source->contents());
  input.SetTotalBytesLimit(INT_MAX);  // Unlimited
  while (true) {
    auto p = input.ReadTagWithCutoff(127);
    int tag = GetTagFieldNumber(p.first);
    WireType wt = GetTagWireType(p.first);
    if (!p.second) {
      return (tag == 0);
    }
    switch (tag) {
      case RecvTensorResponse::kTensorFieldNumber: {
        if (wt != WIRETYPE_LENGTH_DELIMITED) return false;

        int length;
        if (!ReadVarintSizeAsInt(&input, &length)) return false;
        std::pair<protobuf::io::CodedInputStream::Limit, int> p =
            input.IncrementRecursionDepthAndPushLimit(length);
        if (p.second < 0 ||
            !ParseTensorSubmessage(&input, meta_.mutable_tensor())) {
          return false;
        }
        if (!input.DecrementRecursionDepthAndPopLimit(p.first)) {
          return false;
        }
        break;
      }
      case RecvTensorResponse::kIsDeadFieldNumber: {
        uint32 v;
        if ((wt != WIRETYPE_VARINT) || !input.ReadVarint32(&v)) return false;
        meta_.set_is_dead((v != 0) ? true : false);
        break;
      }
      case RecvTensorResponse::kSendStartMicrosFieldNumber: {
        protobuf_uint64 v;
        if ((wt != WIRETYPE_VARINT) || !input.ReadVarint64(&v)) return false;
        meta_.set_send_start_micros(static_cast<int64>(v));
        break;
      }
      case RecvTensorResponse::kTransportOptionsFieldNumber: {
        if ((wt != WIRETYPE_LENGTH_DELIMITED) ||
            !ReadNestedMessage(&input, meta_.mutable_transport_options()))
          return false;
        break;
      }
      default: {
        // Unknown tag, so don't handle we can't handle on the fast path
        return false;
      }
    }
  }

  return false;
}

bool TensorResponse::ParseSlow(Source* source) {
  if (!meta_.ParseFromZeroCopyStream(source->contents())) {
    return false;
  }

  Tensor parsed(meta_.tensor().dtype());
  if (!parsed.FromProto(allocator_, meta_.tensor())) {
    return false;
  }
  tensor_ = std::move(parsed);

  // Reduce memory usage for big tensors.
  {
    TensorProto empty;
    meta_.mutable_tensor()->Swap(&empty);
  }
  meta_.clear_tensor();

  return true;
}

}  // namespace tensorflow
<commit_msg>Also reverting ccedcb<commit_after>/* Copyright 2016 The TensorFlow Authors. 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.
==============================================================================*/

#include "tensorflow/core/distributed_runtime/tensor_coding.h"

#include "google/protobuf/any.pb.h"

#include "tensorflow/core/common_runtime/device.h"
#include "tensorflow/core/framework/tensor.pb.h"
#include "tensorflow/core/framework/tensor_shape.pb.h"

namespace tensorflow {

TensorResponse::Source::~Source() {}

void TensorResponse::Clear() {
  on_host_ = false;
  device_ = nullptr;
  alloc_attrs_ = AllocatorAttributes();
  allocator_ = nullptr;
  already_used_ = false;
  ClearTensor();
}

void TensorResponse::ClearTensor() {
  meta_.Clear();
  tensor_ = Tensor();
}

void TensorResponse::InitAlloc(DeviceBase* d, const AllocatorAttributes& aa) {
  Clear();
  device_ = d;
  alloc_attrs_ = aa;
  const DeviceAttributes& da = d->attributes();
  if (alloc_attrs_.on_host() || da.device_type() == "CPU") {
    on_host_ = true;
  }
  allocator_ = device_->GetAllocator(alloc_attrs_);
}

Status TensorResponse::InitFrom(RecvTensorResponse* response) {
  Status s;
  meta_.Swap(response);
  if (on_host_) {
    if (!tensor_.FromProto(allocator_, meta_.tensor())) {
      s = errors::InvalidArgument("Cannot parse tensor from response");
    }
  } else {
    s = device_->MakeTensorFromProto(meta_.tensor(), alloc_attrs_, &tensor_);
  }
  {
    TensorProto empty;
    meta_.mutable_tensor()->Swap(&empty);
  }
  meta_.clear_tensor();
  return s;
}

void TensorResponse::InitPartial(const RecvTensorResponse& response) {
  // Everything except content is present in *response.  Content will
  // arrive later; allocate a Tensor with appropriate storage for that
  // content.
  meta_ = response;
  TensorShape shape(meta_.tensor().tensor_shape());
  Tensor t(allocator_, meta_.tensor().dtype(), shape);
  tensor_ = std::move(t);
}

Status TensorResponse::ParseFrom(Source* source) {
  if (!on_host_) {
    protobuf::io::CodedInputStream input(source->contents());
    input.SetTotalBytesLimit(INT_MAX, INT_MAX);  // Unlimited

    // Pre-parse into local storage, then delegate to device.
    if (!meta_.ParseFromCodedStream(&input) || !input.ConsumedEntireMessage()) {
      return errors::InvalidArgument("Cannot parse tensor from response");
    }
    Status s =
        device_->MakeTensorFromProto(meta_.tensor(), alloc_attrs_, &tensor_);
    // Reduce memory usage for big tensors.
    {
      TensorProto empty;
      meta_.mutable_tensor()->Swap(&empty);
    }
    meta_.clear_tensor();
    return s;
  }
  if (already_used_) {
    ClearTensor();
  }
  already_used_ = true;
  if (ParseFast(source)) return Status::OK();
  meta_.Clear();
  if (ParseSlow(source)) return Status::OK();
  return errors::InvalidArgument("Cannot parse tensor from response");
}

// Define some helper routines for decoding protocol buffer wire format data
namespace {
// We only need some of the wiretype values for this code
enum WireType {
  WIRETYPE_VARINT = 0,
  WIRETYPE_LENGTH_DELIMITED = 2,
};
inline int GetTagFieldNumber(uint32 tag) { return tag >> 3; }
inline WireType GetTagWireType(uint32 tag) {
  return static_cast<WireType>(tag & 0x7);
}

bool ReadVarintSizeAsInt(protobuf::io::CodedInputStream* input, int* result) {
  protobuf_uint64 v;
  if (input->ReadVarint64(&v) && v <= static_cast<uint64>(INT_MAX)) {
    *result = static_cast<int>(v);
    return true;
  } else {
    return false;
  }
}

bool ReadNestedMessage(protobuf::io::CodedInputStream* input,
                       protobuf::Message* value) {
  int length;
  if (!ReadVarintSizeAsInt(input, &length)) return false;
  std::pair<protobuf::io::CodedInputStream::Limit, int> p =
      input->IncrementRecursionDepthAndPushLimit(length);
  if (p.second < 0 || !value->MergePartialFromCodedStream(input)) return false;
  // Make sure that parsing stopped when the limit was hit, not at an endgroup
  // tag.
  return input->DecrementRecursionDepthAndPopLimit(p.first);
}

}  // namespace

bool TensorResponse::ParseTensorSubmessage(
    protobuf::io::CodedInputStream* input, TensorProto* tensor_meta) {
  bool seen_tensor_content = false;
  while (true) {
    auto p = input->ReadTagWithCutoff(127);
    int tag = GetTagFieldNumber(p.first);
    WireType wt = GetTagWireType(p.first);
    if (!p.second) {
      bool ok = (tag == 0);
      if (ok && !seen_tensor_content) {
        // No tensor content: could be because it's a zero-length tensor
        TensorShape shape(tensor_meta->tensor_shape());
        Tensor t(allocator_, tensor_meta->dtype(), shape);
        tensor_ = std::move(t);
      }
      return ok;
    }
    switch (tag) {
      case TensorProto::kDtypeFieldNumber: {
        uint32 v;
        if ((wt != WIRETYPE_VARINT) || !input->ReadVarint32(&v)) return false;
        if (seen_tensor_content) return false;
        tensor_meta->set_dtype(static_cast<DataType>(static_cast<int>(v)));
        if (!DataTypeCanUseMemcpy(tensor_meta->dtype())) return false;
        break;
      }
      case TensorProto::kTensorShapeFieldNumber: {
        if ((wt != WIRETYPE_LENGTH_DELIMITED) ||
            !ReadNestedMessage(input, tensor_meta->mutable_tensor_shape()))
          return false;
        if (seen_tensor_content) return false;
        break;
      }
      case TensorProto::kVersionNumberFieldNumber: {
        uint32 v;
        if ((wt != WIRETYPE_VARINT) || !input->ReadVarint32(&v)) return false;
        if (seen_tensor_content) return false;
        tensor_meta->set_version_number(static_cast<int32>(v));
        break;
      }
      case TensorProto::kTensorContentFieldNumber: {
        // If we haven't seen the dtype and tensor_shape data first, we can't
        // deal with this in the fast path.
        if (seen_tensor_content) return false;
        if (wt != WIRETYPE_LENGTH_DELIMITED ||
            !tensor_meta->has_tensor_shape()) {
          return false;
        }
        int num_bytes;
        if (!ReadVarintSizeAsInt(input, &num_bytes)) return false;
        seen_tensor_content = true;
        TensorShape shape(tensor_meta->tensor_shape());
        Tensor t(allocator_, tensor_meta->dtype(), shape);
        StringPiece buf = t.tensor_data();
        if (static_cast<size_t>(num_bytes) != buf.size()) return false;
        // TODO(jeff,sanjay): Figure out a way to avoid this copy if
        // the underlying ZeroCopyInputStream data is properly aligned
        // and compatible with what allocator_ wants.
        if (!input->ReadRaw(const_cast<char*>(buf.data()), num_bytes))
          return false;
        tensor_ = std::move(t);
        break;
      }
      default: {
        // Some other tag our fast path code is not prepared to handle.
        // return false.
        return false;
      }
    }
  }
}

bool TensorResponse::ParseFast(Source* source) {
  protobuf::io::CodedInputStream input(source->contents());
  input.SetTotalBytesLimit(INT_MAX, INT_MAX);  // Unlimited
  while (true) {
    auto p = input.ReadTagWithCutoff(127);
    int tag = GetTagFieldNumber(p.first);
    WireType wt = GetTagWireType(p.first);
    if (!p.second) {
      return (tag == 0);
    }
    switch (tag) {
      case RecvTensorResponse::kTensorFieldNumber: {
        if (wt != WIRETYPE_LENGTH_DELIMITED) return false;

        int length;
        if (!ReadVarintSizeAsInt(&input, &length)) return false;
        std::pair<protobuf::io::CodedInputStream::Limit, int> p =
            input.IncrementRecursionDepthAndPushLimit(length);
        if (p.second < 0 ||
            !ParseTensorSubmessage(&input, meta_.mutable_tensor())) {
          return false;
        }
        if (!input.DecrementRecursionDepthAndPopLimit(p.first)) {
          return false;
        }
        break;
      }
      case RecvTensorResponse::kIsDeadFieldNumber: {
        uint32 v;
        if ((wt != WIRETYPE_VARINT) || !input.ReadVarint32(&v)) return false;
        meta_.set_is_dead((v != 0) ? true : false);
        break;
      }
      case RecvTensorResponse::kSendStartMicrosFieldNumber: {
        protobuf_uint64 v;
        if ((wt != WIRETYPE_VARINT) || !input.ReadVarint64(&v)) return false;
        meta_.set_send_start_micros(static_cast<int64>(v));
        break;
      }
      case RecvTensorResponse::kTransportOptionsFieldNumber: {
        if ((wt != WIRETYPE_LENGTH_DELIMITED) ||
            !ReadNestedMessage(&input, meta_.mutable_transport_options()))
          return false;
        break;
      }
      default: {
        // Unknown tag, so don't handle we can't handle on the fast path
        return false;
      }
    }
  }

  return false;
}

bool TensorResponse::ParseSlow(Source* source) {
  if (!meta_.ParseFromZeroCopyStream(source->contents())) {
    return false;
  }

  Tensor parsed(meta_.tensor().dtype());
  if (!parsed.FromProto(allocator_, meta_.tensor())) {
    return false;
  }
  tensor_ = std::move(parsed);

  // Reduce memory usage for big tensors.
  {
    TensorProto empty;
    meta_.mutable_tensor()->Swap(&empty);
  }
  meta_.clear_tensor();

  return true;
}

}  // namespace tensorflow
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#if     qPlatform_Windows
#include    <windows.h>
#include    <shellapi.h>
#include    <shlobj.h>
#elif   qPlatform_POSIX
#include    <unistd.h>
#endif

#include    "../../Characters/StringBuilder.h"
#include    "../../Containers/Set.h"
#if     qPlatform_Windows
#include    "../../Execution/Platform/Windows/Exception.h"
#include    "../../Execution/Platform/Windows/HRESULTErrorException.h"
#endif
#include    "../../Execution/ErrNoException.h"

#include    "../../IO/FileAccessException.h"
#include    "../../IO/FileBusyException.h"
#include    "../../IO/FileFormatException.h"
#include    "../../Memory/SmallStackBuffer.h"

#include    "FileUtils.h"

#include    "FileSystem.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;
using   namespace   Stroika::Foundation::Containers;
using   namespace   Stroika::Foundation::Execution;
using   namespace   Stroika::Foundation::IO;
using   namespace   Stroika::Foundation::IO::FileSystem;

#if     qPlatform_Windows
using   Execution::Platform::Windows::ThrowIfFalseGetLastError;
using   Execution::Platform::Windows::ThrowIfZeroGetLastError;
#endif

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

/*
 ********************************************************************************
 **************************** FileSystem::FileSystem ****************************
 ********************************************************************************
 */
IO::FileSystem::FileSystem  IO::FileSystem::FileSystem::Default ()
{
    static  IO::FileSystem::FileSystem  sThe_;
    return sThe_;
}

bool    IO::FileSystem::FileSystem::Access (const String& fileFullPath, FileAccessMode accessMode) const
{
#if     qPlatform_Windows
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if (attribs == INVALID_FILE_ATTRIBUTES) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if ((attribs == INVALID_FILE_ATTRIBUTES) or (attribs & FILE_ATTRIBUTE_READONLY)) {
            return false;
        }
    }
    return true;
#elif   qPlatform_POSIX
    // Not REALLY right - but an OK hack for now... -- LGP 2011-09-26
    //http://linux.die.net/man/2/access
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        if (access (fileFullPath.AsSDKString().c_str (), R_OK) != 0) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        if (access (fileFullPath.AsSDKString().c_str (), W_OK) != 0) {
            return false;
        }
    }
    return true;
#else
    AssertNotImplemented ();
    return false;
#endif
}

void    IO::FileSystem::FileSystem::CheckAccess (const String& fileFullPath, FileAccessMode accessMode)
{
    // quick hack - not fully implemented - but since advsiory only - not too important...

    if (not Access (fileFullPath, accessMode)) {
        // FOR NOW - MIMIC OLD CODE - BUT FIX TO CHECK READ AND WRITE (AND BOTH) ACCESS DEPENDING ON ARGS) -- LGP 2009-08-15
        Execution::DoThrow (FileAccessException (fileFullPath, accessMode));
    }
}

void    IO::FileSystem::FileSystem::CheckFileAccess (const String& fileFullPath, bool checkCanRead, bool checkCanWrite)
{
    if (checkCanRead and checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eReadWrite);
    }
    else if (checkCanRead) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eRead);
    }
    else if (checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eWrite);
    }
}

String IO::FileSystem::FileSystem::ResolveShortcut (const String& path2FileOrShortcut)
{
#if     qPlatform_Windows
    // @todo WRONG semantics if file doesnt exist. Wed should raise an exception here.
    // But OK if not a shortcut. THEN just rutn the givne file
    //
    //
    // NB: this requires COM, and for now - I don't want the support module depending on the COM module,
    // so just allow this to fail if COM isn't initialized.
    //      -- LGP 2007-09-23
    //
    {
        SHFILEINFO   info;
        memset (&info, 0, sizeof (info));
        if (::SHGetFileInfo (path2FileOrShortcut.AsSDKString ().c_str (), 0, &info, sizeof (info), SHGFI_ATTRIBUTES) == 0)
        {
            return path2FileOrShortcut;
        }
        // not a shortcut?
        if (!(info.dwAttributes & SFGAO_LINK))
        {
            return path2FileOrShortcut;
        }
    }

    // obtain the IShellLink interface
    IShellLink*     psl =   nullptr;
    IPersistFile*   ppf =   nullptr;
    try {
        if (FAILED (::CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl))) {
            return path2FileOrShortcut;
        }
        if (SUCCEEDED (psl->QueryInterface (IID_IPersistFile, (LPVOID*)&ppf))) {
            if (SUCCEEDED (ppf->Load (path2FileOrShortcut.c_str (), STGM_READ))) {
                // Resolve the link, this may post UI to find the link
                if (SUCCEEDED (psl->Resolve(0, SLR_NO_UI))) {
                    TCHAR   path[MAX_PATH + 1];
                    memset (path, 0, sizeof (path));
                    if (SUCCEEDED (psl->GetPath (path, NEltsOf (path), nullptr, 0))) {
                        ppf->Release ();
                        ppf = nullptr;
                        psl->Release ();
                        psl = nullptr;
                        return String::FromSDKString (path);
                    }
                }
            }
        }
    }
    catch (...) {
        if (ppf != nullptr) {
            ppf->Release ();
        }
        if (psl != nullptr) {
            psl->Release ();
        }
        Execution::DoReThrow ();
    }
    if (ppf != nullptr) {
        ppf->Release ();
    }
    if (psl != nullptr) {
        psl->Release ();
    }
    return path2FileOrShortcut;
#else
    Memory::SmallStackBuffer<Characters::SDKChar> buf (1024);
    ssize_t n;
    while ( (n = ::readlink (path2FileOrShortcut.AsSDKString ().c_str (), buf, buf.GetSize ())) == buf.GetSize ()) {
        buf.GrowToSize (buf.GetSize () * 2);
    }
    if (n < 0) {
        auto    e   =   errno;
        if (e == EINVAL) {
            // According to http://linux.die.net/man/2/readlink - this means the target is not a shortcut which is OK
            return path2FileOrShortcut;
        }
        else {
            Execution::errno_ErrorException::DoThrow (e);
        }
    }
    Assert (n <= buf.GetSize ());   // could leave no room for NUL-byte, but not needed
    constexpr   bool    kWorkaroundBuggyCentos5ReturnsNulBytesInBuf_ = true;
    if (kWorkaroundBuggyCentos5ReturnsNulBytesInBuf_) {
        const Characters::SDKChar*  b   =   buf.begin ();
        const Characters::SDKChar*  e   =   b + n;
        const Characters::SDKChar*  i = find (b, e, '\0');
        if (i != e) {
            size_t  newN = i - buf.begin ();
            Assert (newN < n);
            n = newN;
        }
    }
    return String::FromSDKString (SDKString (buf.begin (), buf.begin () + n));
#endif
}

String IO::FileSystem::FileSystem::CanonicalizeName (const String& path2FileOrShortcut, bool throwIfComponentsNotFound)
{
#if     qPlatform_POSIX
    //  We used to call canonicalize_file_name() - but this doesnt work with AIX 7.1/g++4.9.2, and
    //  according to http://man7.org/linux/man-pages/man3/canonicalize_file_name.3.html:
    //  The call canonicalize_file_name(path) is equivalent to the call:
    //      realpath(path, NULL)
    char*   tmp { ::realpath (path2FileOrShortcut.AsSDKString ().c_str (), nullptr) };
    if (tmp == nullptr) {
        errno_ErrorException::DoThrow (errno);
    }
    String  result  { String::FromNarrowSDKString (tmp) };
    free (tmp);
    return result;
#elif   qPlatform_Windows

    // @todo LARGELY UNSTED ROUGH DRAFT - 2015-05-11
    /*
     *  Note:   PathCanonicalize has lots of problems, PathCanonicalizeCh, and
     *  PathCchCanonicalizeEx is better, but only works with windows 8 or later.
     */
    using   Characters::StringBuilder;
    String  tmp =   ResolveShortcut (path2FileOrShortcut);
    StringBuilder sb;
    Components  c   =   GetPathComponents (path2FileOrShortcut);
    if (c.fAbsolutePath == Components::eAbsolutePath) {
        // use UNC notation
        sb += L"\\\\?";
        if (c.fDriveLetter) {
            sb += *c.fDriveLetter;
        }
        else if (c.fServerAndShare) {
            sb += c.fServerAndShare->fServer + L"\\" +  c.fServerAndShare->fShare;
        }
        else {
            Execution::DoThrow (Execution::StringException (L"for absolute path need drive letter or server/share"));
        }
    }
    else {
        if (c.fDriveLetter) {
            sb += *c.fDriveLetter + L":";
        }
    }
    bool    prefixWIthSlash  = false;
    for (String i : c.fPath) {
        if (prefixWIthSlash) {
            sb += L"\\";
        }
        sb += i;
        prefixWIthSlash = true;
    }
    return sb.str ();
#else
    AssertNotImplemented ();
    return path2FileOrShortcut;
#endif
}

String IO::FileSystem::FileSystem::CanonicalizeName (const String& path2FileOrShortcut, const String& relativeToDirectory, bool throwIfComponentsNotFound)
{
    AssertNotImplemented ();
    return path2FileOrShortcut;
}

IO::FileSystem::FileSystem::Components    IO::FileSystem::FileSystem::GetPathComponents (const String& fileName)
{
    // @todo LARGELY UNSTED ROUGH DRAFT - 2015-05-11
    // See http://en.wikipedia.org/wiki/Path_%28computing%29 to write this
#if 0
//windows
C:
    \user\docs\Letter.txt
    / user / docs / Letter.txt
C:
    Letter.txt
    \\Server01\user\docs\Letter.txt
    \\ ? \UNC\Server01\user\docs\Letter.txt
    \\ ? \C : \user\docs\Letter.txt
    C : \user\docs\somefile.ext : alternate_stream_name
    . / inthisdir
    .. / .. / greatgrandparent
#endif
#if 0
// unix
    / home / user / docs / Letter.txt
    . / inthisdir
    .. / .. / greatgrandparent
    ~ / .rcinfo
#endif
    IO::FileSystem::FileSystem::Components  result;
    using   Traversal::Iterator;
    using   Characters::Character;

#if     qPlatform_Windows
    bool    isUNCName = fileName.length () > 2 and fileName.StartsWith (L"\\\\");
    bool    isAbsolutePath = fileName.length () >= 1 and fileName.StartsWith (L"\\");
#else
#endif
#if     qPlatform_Windows
    const   Set<Character>  kSlashChars_ = { '\\', '/' };
#else
    const   Set<Character>  kSlashChars_ = { '/' };
#endif
    Sequence<String>    rawComponents = fileName.Tokenize (kSlashChars_, false);
    Iterator<String>    i   =   rawComponents.begin ();
#if     qPlatform_Windows
    if (isUNCName) {
        // work todo
    }
#endif
    for (; i != rawComponents.end (); ++i) {
        result.fPath.Append (*i);
    }
    AssertNotImplemented ();
    return result;
}

FileOffset_t    IO::FileSystem::FileSystem::GetFileSize (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    Execution::Platform::Windows::ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return fileAttrData.nFileSizeLow + (static_cast<FileOffset_t> (fileAttrData.nFileSizeHigh) << 32);
#else
    AssertNotImplemented ();
    return 0;
#endif
}

DateTime        IO::FileSystem::FileSystem::GetFileLastModificationDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastWriteTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

DateTime    IO::FileSystem::FileSystem::GetFileLastAccessDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastAccessTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

void        IO::FileSystem::FileSystem::RemoveFile (const String& fileName)
{
#if     qPlatform_Windows && qTargetPlatformSDKUseswchar_t
    Execution::ThrowErrNoIfNegative (::_wunlink (fileName.c_str ()));
#else
    Execution::ThrowErrNoIfNegative (::unlink (fileName.AsNarrowSDKString ().c_str ()));
#endif
}

void       IO::FileSystem::FileSystem::RemoveFileIf (const String& fileName)
{
#if     qPlatform_Windows && qTargetPlatformSDKUseswchar_t
    int r = ::_wunlink (fileName.c_str ());
#else
    int r = ::unlink (fileName.AsNarrowSDKString ().c_str ());
#endif
    if (r < 0) {
        if (errno != ENOENT) {
            errno_ErrorException::DoThrow (errno);
        }
    }
}

String  IO::FileSystem::FileSystem::GetCurrentDirectory () const
{
#if     qPlatform_POSIX
    SDKChar buf[PATH_MAX];
    Execution::ThrowErrNoIfNull (::getcwd (buf, NEltsOf (buf)));
    return String::FromSDKString (buf);
#elif   qPlatform_Windows
    SDKChar buf[MAX_PATH];
    ThrowIfZeroGetLastError (::GetCurrentDirectory (NEltsOf (buf), buf));
    return String::FromSDKString (buf);
#else
#endif
}

void    IO::FileSystem::FileSystem::SetCurrentDirectory (const String& newDir)
{
#if     qPlatform_POSIX
    Execution::ThrowErrNoIfNegative (::chdir (newDir.AsNarrowSDKString ().c_str ()));
#elif   qPlatform_Windows
    ::SetCurrentDirectory(newDir.AsSDKString ().c_str ());
#else
#endif
}
<commit_msg>cleanups<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#if     qPlatform_Windows
#include    <windows.h>
#include    <shellapi.h>
#include    <shlobj.h>
#elif   qPlatform_POSIX
#include    <unistd.h>
#endif

#include    "../../Characters/StringBuilder.h"
#include    "../../Containers/Set.h"
#if     qPlatform_Windows
#include    "../../Execution/Platform/Windows/Exception.h"
#include    "../../Execution/Platform/Windows/HRESULTErrorException.h"
#endif
#include    "../../Execution/ErrNoException.h"

#include    "../../IO/FileAccessException.h"
#include    "../../IO/FileBusyException.h"
#include    "../../IO/FileFormatException.h"
#include    "../../Memory/SmallStackBuffer.h"

#include    "FileUtils.h"

#include    "FileSystem.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;
using   namespace   Stroika::Foundation::Containers;
using   namespace   Stroika::Foundation::Execution;
using   namespace   Stroika::Foundation::IO;
using   namespace   Stroika::Foundation::IO::FileSystem;

#if     qPlatform_Windows
using   Execution::Platform::Windows::ThrowIfFalseGetLastError;
using   Execution::Platform::Windows::ThrowIfZeroGetLastError;
#endif

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

/*
 ********************************************************************************
 **************************** FileSystem::FileSystem ****************************
 ********************************************************************************
 */
IO::FileSystem::FileSystem  IO::FileSystem::FileSystem::Default ()
{
    static  IO::FileSystem::FileSystem  sThe_;
    return sThe_;
}

bool    IO::FileSystem::FileSystem::Access (const String& fileFullPath, FileAccessMode accessMode) const
{
#if     qPlatform_Windows
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if (attribs == INVALID_FILE_ATTRIBUTES) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        DWORD attribs = ::GetFileAttributesW (fileFullPath.c_str ());
        if ((attribs == INVALID_FILE_ATTRIBUTES) or (attribs & FILE_ATTRIBUTE_READONLY)) {
            return false;
        }
    }
    return true;
#elif   qPlatform_POSIX
    // Not REALLY right - but an OK hack for now... -- LGP 2011-09-26
    //http://linux.die.net/man/2/access
    if ((accessMode & FileAccessMode::eRead) == FileAccessMode::eRead) {
        if (access (fileFullPath.AsSDKString().c_str (), R_OK) != 0) {
            return false;
        }
    }
    if ((accessMode & FileAccessMode::eWrite) == FileAccessMode::eWrite) {
        if (access (fileFullPath.AsSDKString().c_str (), W_OK) != 0) {
            return false;
        }
    }
    return true;
#else
    AssertNotImplemented ();
    return false;
#endif
}

void    IO::FileSystem::FileSystem::CheckAccess (const String& fileFullPath, FileAccessMode accessMode)
{
    // quick hack - not fully implemented - but since advsiory only - not too important...

    if (not Access (fileFullPath, accessMode)) {
        // FOR NOW - MIMIC OLD CODE - BUT FIX TO CHECK READ AND WRITE (AND BOTH) ACCESS DEPENDING ON ARGS) -- LGP 2009-08-15
        Execution::DoThrow (FileAccessException (fileFullPath, accessMode));
    }
}

void    IO::FileSystem::FileSystem::CheckFileAccess (const String& fileFullPath, bool checkCanRead, bool checkCanWrite)
{
    if (checkCanRead and checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eReadWrite);
    }
    else if (checkCanRead) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eRead);
    }
    else if (checkCanWrite) {
        CheckAccess (fileFullPath, IO::FileAccessMode::eWrite);
    }
}

String IO::FileSystem::FileSystem::ResolveShortcut (const String& path2FileOrShortcut)
{
#if     qPlatform_Windows
    // @todo WRONG semantics if file doesnt exist. Wed should raise an exception here.
    // But OK if not a shortcut. THEN just rutn the givne file
    //
    //
    // NB: this requires COM, and for now - I don't want the support module depending on the COM module,
    // so just allow this to fail if COM isn't initialized.
    //      -- LGP 2007-09-23
    //
    {
        SHFILEINFO   info;
        memset (&info, 0, sizeof (info));
        if (::SHGetFileInfo (path2FileOrShortcut.AsSDKString ().c_str (), 0, &info, sizeof (info), SHGFI_ATTRIBUTES) == 0)
        {
            return path2FileOrShortcut;
        }
        // not a shortcut?
        if (!(info.dwAttributes & SFGAO_LINK))
        {
            return path2FileOrShortcut;
        }
    }

    // obtain the IShellLink interface
    IShellLink*     psl =   nullptr;
    IPersistFile*   ppf =   nullptr;
    try {
        if (FAILED (::CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl))) {
            return path2FileOrShortcut;
        }
        if (SUCCEEDED (psl->QueryInterface (IID_IPersistFile, (LPVOID*)&ppf))) {
            if (SUCCEEDED (ppf->Load (path2FileOrShortcut.c_str (), STGM_READ))) {
                // Resolve the link, this may post UI to find the link
                if (SUCCEEDED (psl->Resolve(0, SLR_NO_UI))) {
                    TCHAR   path[MAX_PATH + 1];
                    memset (path, 0, sizeof (path));
                    if (SUCCEEDED (psl->GetPath (path, NEltsOf (path), nullptr, 0))) {
                        ppf->Release ();
                        ppf = nullptr;
                        psl->Release ();
                        psl = nullptr;
                        return String::FromSDKString (path);
                    }
                }
            }
        }
    }
    catch (...) {
        if (ppf != nullptr) {
            ppf->Release ();
        }
        if (psl != nullptr) {
            psl->Release ();
        }
        Execution::DoReThrow ();
    }
    if (ppf != nullptr) {
        ppf->Release ();
    }
    if (psl != nullptr) {
        psl->Release ();
    }
    return path2FileOrShortcut;
#else
    Memory::SmallStackBuffer<Characters::SDKChar> buf (1024);
    ssize_t n;
    while ( (n = ::readlink (path2FileOrShortcut.AsSDKString ().c_str (), buf, buf.GetSize ())) == buf.GetSize ()) {
        buf.GrowToSize (buf.GetSize () * 2);
    }
    if (n < 0) {
        auto    e   =   errno;
        if (e == EINVAL) {
            // According to http://linux.die.net/man/2/readlink - this means the target is not a shortcut which is OK
            return path2FileOrShortcut;
        }
        else {
            Execution::errno_ErrorException::DoThrow (e);
        }
    }
    Assert (n <= buf.GetSize ());   // could leave no room for NUL-byte, but not needed
    constexpr   bool    kWorkaroundBuggyCentos5ReturnsNulBytesInBuf_ = true;
    if (kWorkaroundBuggyCentos5ReturnsNulBytesInBuf_) {
        const Characters::SDKChar*  b   =   buf.begin ();
        const Characters::SDKChar*  e   =   b + n;
        const Characters::SDKChar*  i = find (b, e, '\0');
        if (i != e) {
            size_t  newN = i - buf.begin ();
            Assert (newN < n);
            n = newN;
        }
    }
    return String::FromSDKString (SDKString (buf.begin (), buf.begin () + n));
#endif
}

String IO::FileSystem::FileSystem::CanonicalizeName (const String& path2FileOrShortcut, bool throwIfComponentsNotFound)
{
#if     qPlatform_POSIX
    //  We used to call canonicalize_file_name() - but this doesnt work with AIX 7.1/g++4.9.2, and
    //  according to http://man7.org/linux/man-pages/man3/canonicalize_file_name.3.html:
    //  The call canonicalize_file_name(path) is equivalent to the call:
    //      realpath(path, NULL)
    char*   tmp { ::realpath (path2FileOrShortcut.AsSDKString ().c_str (), nullptr) };
    if (tmp == nullptr) {
        errno_ErrorException::DoThrow (errno);
    }
    String  result  { String::FromNarrowSDKString (tmp) };
    free (tmp);
    return result;
#elif   qPlatform_Windows

    // @todo LARGELY UNSTED ROUGH DRAFT - 2015-05-11
    /*
     *  Note:   PathCanonicalize has lots of problems, PathCanonicalizeCh, and
     *  PathCchCanonicalizeEx is better, but only works with windows 8 or later.
     */
    using   Characters::StringBuilder;
    String  tmp =   ResolveShortcut (path2FileOrShortcut);
    StringBuilder sb;
    Components  c   =   GetPathComponents (path2FileOrShortcut);
    if (c.fAbsolutePath == Components::eAbsolutePath) {
        // use UNC notation
        sb += L"\\\\?";
        if (c.fDriveLetter) {
            sb += *c.fDriveLetter;
        }
        else if (c.fServerAndShare) {
            sb += c.fServerAndShare->fServer + L"\\" +  c.fServerAndShare->fShare;
        }
        else {
            Execution::DoThrow (Execution::StringException (L"for absolute path need drive letter or server/share"));
        }
    }
    else {
        if (c.fDriveLetter) {
            sb += *c.fDriveLetter + L":";
        }
    }
    bool    prefixWIthSlash  = false;
    for (String i : c.fPath) {
        if (prefixWIthSlash) {
            sb += L"\\";
        }
        sb += i;
        prefixWIthSlash = true;
    }
    return sb.str ();
#else
    AssertNotImplemented ();
    return path2FileOrShortcut;
#endif
}

String IO::FileSystem::FileSystem::CanonicalizeName (const String& path2FileOrShortcut, const String& relativeToDirectory, bool throwIfComponentsNotFound)
{
    AssertNotImplemented ();
    return path2FileOrShortcut;
}

IO::FileSystem::FileSystem::Components    IO::FileSystem::FileSystem::GetPathComponents (const String& fileName)
{
    // @todo LARGELY UNSTED ROUGH DRAFT - 2015-05-11
    // See http://en.wikipedia.org/wiki/Path_%28computing%29 to write this
#if 0
//windows
C:
    \user\docs\Letter.txt
    / user / docs / Letter.txt
C:
    Letter.txt
    \\Server01\user\docs\Letter.txt
    \\ ? \UNC\Server01\user\docs\Letter.txt
    \\ ? \C : \user\docs\Letter.txt
    C : \user\docs\somefile.ext : alternate_stream_name
    . / inthisdir
    .. / .. / greatgrandparent
#endif
#if 0
// unix
    / home / user / docs / Letter.txt
    . / inthisdir
    .. / .. / greatgrandparent
    ~ / .rcinfo
#endif
    IO::FileSystem::FileSystem::Components  result;
    using   Traversal::Iterator;
    using   Characters::Character;

#if     qPlatform_Windows
    bool    isUNCName = fileName.length () > 2 and fileName.StartsWith (L"\\\\");
    bool    isAbsolutePath = fileName.length () >= 1 and fileName.StartsWith (L"\\");
#else
#endif
#if     qPlatform_Windows
    const   Set<Character>  kSlashChars_ = { '\\', '/' };
#else
    const   Set<Character>  kSlashChars_ = { '/' };
#endif
    Sequence<String>    rawComponents = fileName.Tokenize (kSlashChars_, false);
    Iterator<String>    i   =   rawComponents.begin ();
#if     qPlatform_Windows
    if (isUNCName) {
        // work todo
    }
#endif
    for (; i != rawComponents.end (); ++i) {
        result.fPath.Append (*i);
    }
    AssertNotImplemented ();
    return result;
}

FileOffset_t    IO::FileSystem::FileSystem::GetFileSize (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    Execution::Platform::Windows::ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return fileAttrData.nFileSizeLow + (static_cast<FileOffset_t> (fileAttrData.nFileSizeHigh) << 32);
#else
    AssertNotImplemented ();
    return 0;
#endif
}

DateTime        IO::FileSystem::FileSystem::GetFileLastModificationDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastWriteTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

DateTime    IO::FileSystem::FileSystem::GetFileLastAccessDate (const String& fileName)
{
#if     qPlatform_Windows
    WIN32_FILE_ATTRIBUTE_DATA   fileAttrData;
    (void)::memset (&fileAttrData, 0, sizeof (fileAttrData));
    ThrowIfFalseGetLastError (::GetFileAttributesExW (fileName.c_str (), GetFileExInfoStandard, &fileAttrData));
    return DateTime (fileAttrData.ftLastAccessTime);
#else
    AssertNotImplemented ();
    return DateTime ();
#endif
}

void        IO::FileSystem::FileSystem::RemoveFile (const String& fileName)
{
#if     qPlatform_Windows && qTargetPlatformSDKUseswchar_t
    Execution::ThrowErrNoIfNegative (::_wunlink (fileName.c_str ()));
#else
    Execution::ThrowErrNoIfNegative (::unlink (fileName.AsNarrowSDKString ().c_str ()));
#endif
}

void       IO::FileSystem::FileSystem::RemoveFileIf (const String& fileName)
{
#if     qPlatform_Windows && qTargetPlatformSDKUseswchar_t
    int r = ::_wunlink (fileName.c_str ());
#else
    int r = ::unlink (fileName.AsNarrowSDKString ().c_str ());
#endif
    if (r < 0) {
        if (errno != ENOENT) {
            errno_ErrorException::DoThrow (errno);
        }
    }
}

String  IO::FileSystem::FileSystem::GetCurrentDirectory () const
{
#if     qPlatform_POSIX
    SDKChar buf[PATH_MAX];
    Execution::ThrowErrNoIfNull (::getcwd (buf, NEltsOf (buf)));
    return String::FromSDKString (buf);
#elif   qPlatform_Windows
    SDKChar buf[MAX_PATH];
    ThrowIfZeroGetLastError (::GetCurrentDirectory (NEltsOf (buf), buf));
    return String::FromSDKString (buf);
#else
    AssertNotReached ();
#endif
}

void    IO::FileSystem::FileSystem::SetCurrentDirectory (const String& newDir)
{
#if     qPlatform_POSIX
    Execution::ThrowErrNoIfNegative (::chdir (newDir.AsNarrowSDKString ().c_str ()));
#elif   qPlatform_Windows
    ::SetCurrentDirectory(newDir.AsSDKString ().c_str ());
#else
    AssertNotReached ();
#endif
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "otbSamplingRateCalculatorList.h"
#include "otbMacro.h"

namespace otb
{

void
SamplingRateCalculatorList
::SetNthClassCount(unsigned int index,const ClassCountMapType &map)
{
  if (index >= this->Size())
    {
    unsigned int currentSize = this->Size();
    for (unsigned int i = currentSize ; i < (index+1) ; i++ )
      {
      this->PushBack(SamplingRateCalculator::New());
      }
    }
  this->GetNthElement(index)->SetClassCount(map);
}

const SamplingRateCalculatorList::MapRateType &
SamplingRateCalculatorList
::GetRatesByClass(unsigned int index)
{
  if (index >= this->Size())
    {
    itkGenericExceptionMacro("Requesting an index ("<<index<<") larger than list size ("<<this->Size()<<")");
    }
  return this->GetNthElement(index)->GetRatesByClass();
}

void
SamplingRateCalculatorList
::ClearRates(void)
{
  for (unsigned int i=0 ; i<this->Size() ; i++)
    {
    this->GetNthElement(i)->ClearRates();
    }
}

void
SamplingRateCalculatorList
::SetAllSamples(PartitionType t)
{
  this->UpdateGlobalCounts();
  switch (t)
    {
    case PROPORTIONAL:
    case EQUAL:
    case CUSTOM:
      {
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetAllSamples();
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::SetMinimumNbOfSamplesByClass(PartitionType t)
{
  this->UpdateGlobalCounts();
  switch (t)
    {
    case PROPORTIONAL:
    case EQUAL:
      {
      // Use the smallest class globally to derive the constant needed
      unsigned long smallest = itk::NumericTraits<unsigned long>::max();
      ClassCountMapType::iterator it = m_GlobalCountMap.begin();
      for (; it != m_GlobalCountMap.end() ; ++it)
        {
        if (smallest > it->second && it->second > 0UL)
          {
          smallest = it->second;
          }
        }
      if (smallest == itk::NumericTraits<unsigned long>::max())
        {
        otbWarningMacro("All classes are empty !");
        smallest = 0UL;
        }
      std::vector<unsigned long> needed;
      needed.push_back(smallest);
      this->SetNbOfSamplesAllClasses(needed,t);
      break;
      }
    case CUSTOM:
      {
      // Use the smallest class in each input
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetMinimumNbOfSamplesByClass();
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::SetNbOfSamplesAllClasses(std::vector<unsigned long> &nb, PartitionType t)
{
  if (nb.size() == 0)
    {
    itkGenericExceptionMacro("No number of samples given");
    }
  this->UpdateGlobalCounts();
  ClassCountMapType::const_iterator it;
  MapRateType::const_iterator curIt;
  ClassCountMapType needed;
  switch (t)
    {
    case PROPORTIONAL:
      {
      unsigned long curTotal;
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        needed.clear();
        for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it)
          {
          curIt = this->GetNthElement(i)->GetRatesByClass().find(it->first);
          if (curIt != this->GetNthElement(i)->GetRatesByClass().end() &&
              it->second > 0UL)
            {
            curTotal = (curIt->second).Tot;
            needed[it->first] = static_cast<unsigned long>(vcl_floor(
              static_cast<double>(nb[0]) *
              static_cast<double>(curTotal) /
              static_cast<double>(it->second)));
            }
          else
            {
            needed[it->first] = 0UL;
            }
          }
        this->GetNthElement(i)->SetNbOfSamplesByClass(needed);
        }
      break;
      }
    case EQUAL:
      {
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetNbOfSamplesAllClasses(
          static_cast<unsigned long>(vcl_floor(
            (double)nb[0] / (double)this->Size())));
        }
      break;
      }
    case CUSTOM:
      {
      if (nb.size() < this->Size())
        {
        itkGenericExceptionMacro("Not enough values present to set custom requested numbers in all inputs");
        }
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetNbOfSamplesAllClasses(nb[i]);
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::SetNbOfSamplesByClass(const std::vector<ClassCountMapType> &required, PartitionType t)
{
  if (required.size() == 0)
    {
    itkGenericExceptionMacro("No number of samples given");
    }
  this->UpdateGlobalCounts();
  MapRateType::const_iterator curIt;
  ClassCountMapType::const_iterator it;
  ClassCountMapType::const_iterator inputIt;
  ClassCountMapType needed;
  switch (t)
    {
    case PROPORTIONAL:
      {
      unsigned long curTotal;
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        needed.clear();
        for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it)
          {
          curIt = this->GetNthElement(i)->GetRatesByClass().find(it->first);
          inputIt = required[0].find(it->first);
          if (curIt != this->GetNthElement(i)->GetRatesByClass().end() &&
              inputIt != required[0].end() &&
              it->second > 0UL)
            {
            curTotal = (curIt->second).Tot;
            needed[it->first] = static_cast<unsigned long>(vcl_floor(
              static_cast<double>(inputIt->second) *
              static_cast<double>(curTotal) /
              static_cast<double>(it->second)));
            }
          else
            {
            needed[it->first] = 0UL;
            }
          }
        this->GetNthElement(i)->SetNbOfSamplesByClass(needed);
        }
      break;
      }
    case EQUAL:
      {
      unsigned long curTotal;
      unsigned long curNeeded;
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        needed.clear();
        for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it)
          {
          curIt = this->GetNthElement(i)->GetRatesByClass().find(it->first);
          inputIt = required[0].find(it->first);
          if (curIt != this->GetNthElement(i)->GetRatesByClass().end() &&
              inputIt != required[0].end() &&
              it->second > 0UL)
            {
            curTotal = (curIt->second).Tot;
            curNeeded = static_cast<unsigned long>(vcl_floor(
              static_cast<double>(inputIt->second) /
              static_cast<double>(this->Size())));
            needed[it->first] = std::min(curTotal,curNeeded);
            }
          else
            {
            needed[it->first] = 0UL;
            }
          }
        this->GetNthElement(i)->SetNbOfSamplesByClass(needed);
        }
      break;
      }
    case CUSTOM:
      {
      if (required.size() < this->Size())
        {
        itkGenericExceptionMacro("Not enough values present to set custom requested numbers in all inputs");
        }
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetNbOfSamplesByClass(required[i]);
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::UpdateGlobalCounts()
{
  m_GlobalCountMap.clear();
  for (unsigned int i=0 ; i<this->Size() ; i++)
    {
    const MapRateType &rates = this->GetNthElement(i)->GetRatesByClass();
    MapRateType::const_iterator it = rates.begin();
    for (; it != rates.end() ; ++it)
      {
      if (m_GlobalCountMap.count(it->first))
        {
        m_GlobalCountMap[it->first] += it->second.Tot;
        }
      else
        {
        m_GlobalCountMap[it->first] = it->second.Tot;
        }
      }
    }
}

} // end of namespace otb

<commit_msg>ENH: improve SamplingRateCalculatorList implementation<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "otbSamplingRateCalculatorList.h"
#include "otbMacro.h"

namespace otb
{

void
SamplingRateCalculatorList
::SetNthClassCount(unsigned int index,const ClassCountMapType &map)
{
  if (index >= this->Size())
    {
    unsigned int currentSize = this->Size();
    for (unsigned int i = currentSize ; i < (index+1) ; i++ )
      {
      this->PushBack(SamplingRateCalculator::New());
      }
    }
  this->GetNthElement(index)->SetClassCount(map);
}

const SamplingRateCalculatorList::MapRateType &
SamplingRateCalculatorList
::GetRatesByClass(unsigned int index)
{
  if (index >= this->Size())
    {
    itkGenericExceptionMacro("Requesting an index ("<<index<<") larger than list size ("<<this->Size()<<")");
    }
  return this->GetNthElement(index)->GetRatesByClass();
}

void
SamplingRateCalculatorList
::ClearRates(void)
{
  for (unsigned int i=0 ; i<this->Size() ; i++)
    {
    this->GetNthElement(i)->ClearRates();
    }
}

void
SamplingRateCalculatorList
::SetAllSamples(PartitionType t)
{
  this->UpdateGlobalCounts();
  switch (t)
    {
    case PROPORTIONAL:
    case EQUAL:
    case CUSTOM:
      {
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetAllSamples();
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::SetMinimumNbOfSamplesByClass(PartitionType t)
{
  this->UpdateGlobalCounts();
  switch (t)
    {
    case PROPORTIONAL:
    case EQUAL:
      {
      // Use the smallest class globally to derive the constant needed
      unsigned long smallest = itk::NumericTraits<unsigned long>::max();
      ClassCountMapType::iterator it = m_GlobalCountMap.begin();
      for (; it != m_GlobalCountMap.end() ; ++it)
        {
        if (smallest > it->second && it->second > 0UL)
          {
          smallest = it->second;
          }
        }
      if (smallest == itk::NumericTraits<unsigned long>::max())
        {
        otbWarningMacro("All classes are empty !");
        smallest = 0UL;
        }
      std::vector<unsigned long> needed;
      needed.push_back(smallest);
      this->SetNbOfSamplesAllClasses(needed,t);
      break;
      }
    case CUSTOM:
      {
      // Use the smallest class in each input
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetMinimumNbOfSamplesByClass();
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::SetNbOfSamplesAllClasses(std::vector<unsigned long> &nb, PartitionType t)
{
  if (nb.empty())
    {
    itkGenericExceptionMacro("No number of samples given");
    }
  this->UpdateGlobalCounts();
  ClassCountMapType::const_iterator it;
  ClassCountMapType needed;
  switch (t)
    {
    case PROPORTIONAL:
      {
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        needed.clear();
        for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it)
          {
          const MapRateType& rates = this->GetNthElement(i)->GetRatesByClass();
          MapRateType::const_iterator curIt = rates.find(it->first);
          if (curIt != rates.end() &&
              it->second > 0UL)
            {
            unsigned long curTotal = (curIt->second).Tot;
            needed[it->first] = static_cast<unsigned long>(vcl_floor(
              static_cast<double>(nb[0]) *
              static_cast<double>(curTotal) /
              static_cast<double>(it->second)));
            }
          else
            {
            needed[it->first] = 0UL;
            }
          }
        this->GetNthElement(i)->SetNbOfSamplesByClass(needed);
        }
      break;
      }
    case EQUAL:
      {
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetNbOfSamplesAllClasses(
          static_cast<unsigned long>(vcl_floor(
            (double)nb[0] / (double)this->Size())));
        }
      break;
      }
    case CUSTOM:
      {
      if (nb.size() < this->Size())
        {
        itkGenericExceptionMacro("Not enough values present to set custom requested numbers in all inputs");
        }
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetNbOfSamplesAllClasses(nb[i]);
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::SetNbOfSamplesByClass(const std::vector<ClassCountMapType> &required, PartitionType t)
{
  if (required.empty())
    {
    itkGenericExceptionMacro("No number of samples given");
    }
  this->UpdateGlobalCounts();
  ClassCountMapType::const_iterator it;
  ClassCountMapType::const_iterator inputIt;
  ClassCountMapType needed;
  switch (t)
    {
    case PROPORTIONAL:
      {
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        needed.clear();
        for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it)
          {
          const MapRateType &rates = this->GetNthElement(i)->GetRatesByClass();
          MapRateType::const_iterator curIt = rates.find(it->first);
          inputIt = required[0].find(it->first);
          if (curIt != rates.end() &&
              inputIt != required[0].end() &&
              it->second > 0UL)
            {
            unsigned long curTotal = (curIt->second).Tot;
            needed[it->first] = static_cast<unsigned long>(vcl_floor(
              static_cast<double>(inputIt->second) *
              static_cast<double>(curTotal) /
              static_cast<double>(it->second)));
            }
          else
            {
            needed[it->first] = 0UL;
            }
          }
        this->GetNthElement(i)->SetNbOfSamplesByClass(needed);
        }
      break;
      }
    case EQUAL:
      {
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        needed.clear();
        for (it = m_GlobalCountMap.begin(); it != m_GlobalCountMap.end() ; ++it)
          {
          const MapRateType &rates = this->GetNthElement(i)->GetRatesByClass();
          MapRateType::const_iterator curIt = rates.find(it->first);
          inputIt = required[0].find(it->first);
          if (curIt != rates.end() &&
              inputIt != required[0].end() &&
              it->second > 0UL)
            {
            unsigned long curTotal = (curIt->second).Tot;
            unsigned long curNeeded = static_cast<unsigned long>(vcl_floor(
              static_cast<double>(inputIt->second) /
              static_cast<double>(this->Size())));
            needed[it->first] = std::min(curTotal,curNeeded);
            }
          else
            {
            needed[it->first] = 0UL;
            }
          }
        this->GetNthElement(i)->SetNbOfSamplesByClass(needed);
        }
      break;
      }
    case CUSTOM:
      {
      if (required.size() < this->Size())
        {
        itkGenericExceptionMacro("Not enough values present to set custom requested numbers in all inputs");
        }
      for (unsigned int i=0 ; i<this->Size() ; i++)
        {
        this->GetNthElement(i)->SetNbOfSamplesByClass(required[i]);
        }
      break;
      }
    default:
      itkGenericExceptionMacro("Unknown partition mode");
      break;
    }
}

void
SamplingRateCalculatorList
::UpdateGlobalCounts()
{
  m_GlobalCountMap.clear();
  for (unsigned int i=0 ; i<this->Size() ; i++)
    {
    const MapRateType &rates = this->GetNthElement(i)->GetRatesByClass();
    MapRateType::const_iterator it = rates.begin();
    for (; it != rates.end() ; ++it)
      {
      m_GlobalCountMap[it->first] += it->second.Tot;
      }
    }
}

} // end of namespace otb

<|endoftext|>


<commit_before>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"
#include "base/safe_strerror_posix.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>

#define USE_HISTORICAL_STRERRO_R (defined(__GLIBC__) || defined(OS_NACL))

#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
// GCC will complain about the unused second wrap function unless we tell it
// that we meant for them to be potentially unused, which is exactly what this
// attribute is for.
#define POSSIBLY_UNUSED __attribute__((unused))
#else
#define POSSIBLY_UNUSED
#endif

#if USE_HISTORICAL_STRERRO_R
// glibc has two strerror_r functions: a historical GNU-specific one that
// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
// that returns int. This wraps the GNU-specific one.
static void POSSIBLY_UNUSED wrap_posix_strerror_r(
    char *(*strerror_r_ptr)(int, char *, size_t),
    int err,
    char *buf,
    size_t len) {
  // GNU version.
  char *rc = (*strerror_r_ptr)(err, buf, len);
  if (rc != buf) {
    // glibc did not use buf and returned a static string instead. Copy it
    // into buf.
    buf[0] = '\0';
    strncat(buf, rc, len - 1);
  }
  // The GNU version never fails. Unknown errors get an "unknown error" message.
  // The result is always null terminated.
}
#endif  // USE_HISTORICAL_STRERRO_R

// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
// does not define the behaviour for some of the edge cases, so we wrap it to
// guarantee that they are handled. This is compiled on all POSIX platforms, but
// it will only be used on Linux if the POSIX strerror_r implementation is
// being used (see below).
static void POSSIBLY_UNUSED wrap_posix_strerror_r(
    int (*strerror_r_ptr)(int, char *, size_t),
    int err,
    char *buf,
    size_t len) {
  int old_errno = errno;
  // Have to cast since otherwise we get an error if this is the GNU version
  // (but in such a scenario this function is never called). Sadly we can't use
  // C++-style casts because the appropriate one is reinterpret_cast but it's
  // considered illegal to reinterpret_cast a type to itself, so we get an
  // error in the opposite case.
  int result = (*strerror_r_ptr)(err, buf, len);
  if (result == 0) {
    // POSIX is vague about whether the string will be terminated, although
    // it indirectly implies that typically ERANGE will be returned, instead
    // of truncating the string. We play it safe by always terminating the
    // string explicitly.
    buf[len - 1] = '\0';
  } else {
    // Error. POSIX is vague about whether the return value is itself a system
    // error code or something else. On Linux currently it is -1 and errno is
    // set. On BSD-derived systems it is a system error and errno is unchanged.
    // We try and detect which case it is so as to put as much useful info as
    // we can into our message.
    int strerror_error;  // The error encountered in strerror
    int new_errno = errno;
    if (new_errno != old_errno) {
      // errno was changed, so probably the return value is just -1 or something
      // else that doesn't provide any info, and errno is the error.
      strerror_error = new_errno;
    } else {
      // Either the error from strerror_r was the same as the previous value, or
      // errno wasn't used. Assume the latter.
      strerror_error = result;
    }
    // snprintf truncates and always null-terminates.
    snprintf(buf,
             len,
             "Error %d while retrieving error %d",
             strerror_error,
             err);
  }
  errno = old_errno;
}

void safe_strerror_r(int err, char *buf, size_t len) {
  if (buf == NULL || len <= 0) {
    return;
  }
  // If using glibc (i.e., Linux), the compiler will automatically select the
  // appropriate overloaded function based on the function type of strerror_r.
  // The other one will be elided from the translation unit since both are
  // static.
  wrap_posix_strerror_r(&strerror_r, err, buf, len);
}

std::string safe_strerror(int err) {
  const int buffer_size = 256;
  char buf[buffer_size];
  safe_strerror_r(err, buf, sizeof(buf));
  return std::string(buf);
}
<commit_msg>Cherrypick ac38465: Handle bionic in safe_strerror_posix as well as glibc.<commit_after>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"
#include "base/safe_strerror_posix.h"

#include <errno.h>
#include <stdio.h>
#include <string.h>

#define USE_HISTORICAL_STRERRO_R (defined(__GLIBC__) || defined(__BIONIC__) || \
                                  defined(OS_NACL))

#if USE_HISTORICAL_STRERRO_R && defined(__GNUC__)
// GCC will complain about the unused second wrap function unless we tell it
// that we meant for them to be potentially unused, which is exactly what this
// attribute is for.
#define POSSIBLY_UNUSED __attribute__((unused))
#else
#define POSSIBLY_UNUSED
#endif

#if USE_HISTORICAL_STRERRO_R
// glibc has two strerror_r functions: a historical GNU-specific one that
// returns type char *, and a POSIX.1-2001 compliant one available since 2.3.4
// that returns int. This wraps the GNU-specific one.
static void POSSIBLY_UNUSED wrap_posix_strerror_r(
    char *(*strerror_r_ptr)(int, char *, size_t),
    int err,
    char *buf,
    size_t len) {
  // GNU version.
  char *rc = (*strerror_r_ptr)(err, buf, len);
  if (rc != buf) {
    // glibc did not use buf and returned a static string instead. Copy it
    // into buf.
    buf[0] = '\0';
    strncat(buf, rc, len - 1);
  }
  // The GNU version never fails. Unknown errors get an "unknown error" message.
  // The result is always null terminated.
}
#endif  // USE_HISTORICAL_STRERRO_R

// Wrapper for strerror_r functions that implement the POSIX interface. POSIX
// does not define the behaviour for some of the edge cases, so we wrap it to
// guarantee that they are handled. This is compiled on all POSIX platforms, but
// it will only be used on Linux if the POSIX strerror_r implementation is
// being used (see below).
static void POSSIBLY_UNUSED wrap_posix_strerror_r(
    int (*strerror_r_ptr)(int, char *, size_t),
    int err,
    char *buf,
    size_t len) {
  int old_errno = errno;
  // Have to cast since otherwise we get an error if this is the GNU version
  // (but in such a scenario this function is never called). Sadly we can't use
  // C++-style casts because the appropriate one is reinterpret_cast but it's
  // considered illegal to reinterpret_cast a type to itself, so we get an
  // error in the opposite case.
  int result = (*strerror_r_ptr)(err, buf, len);
  if (result == 0) {
    // POSIX is vague about whether the string will be terminated, although
    // it indirectly implies that typically ERANGE will be returned, instead
    // of truncating the string. We play it safe by always terminating the
    // string explicitly.
    buf[len - 1] = '\0';
  } else {
    // Error. POSIX is vague about whether the return value is itself a system
    // error code or something else. On Linux currently it is -1 and errno is
    // set. On BSD-derived systems it is a system error and errno is unchanged.
    // We try and detect which case it is so as to put as much useful info as
    // we can into our message.
    int strerror_error;  // The error encountered in strerror
    int new_errno = errno;
    if (new_errno != old_errno) {
      // errno was changed, so probably the return value is just -1 or something
      // else that doesn't provide any info, and errno is the error.
      strerror_error = new_errno;
    } else {
      // Either the error from strerror_r was the same as the previous value, or
      // errno wasn't used. Assume the latter.
      strerror_error = result;
    }
    // snprintf truncates and always null-terminates.
    snprintf(buf,
             len,
             "Error %d while retrieving error %d",
             strerror_error,
             err);
  }
  errno = old_errno;
}

void safe_strerror_r(int err, char *buf, size_t len) {
  if (buf == NULL || len <= 0) {
    return;
  }
  // If using glibc (i.e., Linux), the compiler will automatically select the
  // appropriate overloaded function based on the function type of strerror_r.
  // The other one will be elided from the translation unit since both are
  // static.
  wrap_posix_strerror_r(&strerror_r, err, buf, len);
}

std::string safe_strerror(int err) {
  const int buffer_size = 256;
  char buf[buffer_size];
  safe_strerror_r(err, buf, sizeof(buf));
  return std::string(buf);
}
<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/shared_memory.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/platform_thread.h"
#include "base/safe_strerror_posix.h"
#include "base/utf_string_conversions.h"

namespace base {

namespace {
// Paranoia. Semaphores and shared memory segments should live in different
// namespaces, but who knows what's out there.
const char kSemaphoreSuffix[] = "-sem";
}

SharedMemory::SharedMemory()
    : mapped_file_(-1),
      inode_(0),
      memory_(NULL),
      read_only_(false),
      max_size_(0) {
}

SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
    : mapped_file_(handle.fd),
      inode_(0),
      memory_(NULL),
      read_only_(read_only),
      max_size_(0) {
  struct stat st;
  if (fstat(handle.fd, &st) == 0) {
    // If fstat fails, then the file descriptor is invalid and we'll learn this
    // fact when Map() fails.
    inode_ = st.st_ino;
  }
}

SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
                           ProcessHandle process)
    : mapped_file_(handle.fd),
      memory_(NULL),
      read_only_(read_only),
      max_size_(0) {
  // We don't handle this case yet (note the ignored parameter); let's die if
  // someone comes calling.
  NOTREACHED();
}

SharedMemory::~SharedMemory() {
  Close();
}

// static
bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
  return handle.fd >= 0;
}

// static
SharedMemoryHandle SharedMemory::NULLHandle() {
  return SharedMemoryHandle();
}

// static
void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
  DCHECK(handle.fd >= 0);
  close(handle.fd);
}

bool SharedMemory::Create(const std::string& name, bool read_only,
                          bool open_existing, uint32 size) {
  read_only_ = read_only;

  int posix_flags = 0;
  posix_flags |= read_only ? O_RDONLY : O_RDWR;
  if (!open_existing || mapped_file_ <= 0)
    posix_flags |= O_CREAT;

  if (!CreateOrOpen(name, posix_flags, size))
    return false;

  max_size_ = size;
  return true;
}

// Our current implementation of shmem is with mmap()ing of files.
// These files need to be deleted explicitly.
// In practice this call is only needed for unit tests.
bool SharedMemory::Delete(const std::string& name) {
  FilePath path;
  if (!FilePathForMemoryName(name, &path))
    return false;

  if (file_util::PathExists(path)) {
    return file_util::Delete(path, false);
  }

  // Doesn't exist, so success.
  return true;
}

bool SharedMemory::Open(const std::string& name, bool read_only) {
  read_only_ = read_only;

  int posix_flags = 0;
  posix_flags |= read_only ? O_RDONLY : O_RDWR;

  return CreateOrOpen(name, posix_flags, 0);
}

// For the given shmem named |mem_name|, return a filename to mmap()
// (and possibly create).  Modifies |filename|.  Return false on
// error, or true of we are happy.
bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
                                         FilePath* path) {
  // mem_name will be used for a filename; make sure it doesn't
  // contain anything which will confuse us.
  DCHECK(mem_name.find('/') == std::string::npos);
  DCHECK(mem_name.find('\0') == std::string::npos);

  FilePath temp_dir;
  if (!file_util::GetShmemTempDir(&temp_dir))
    return false;

  *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name);
  return true;
}

// Chromium mostly only use the unique/private shmem as specified by
// "name == L"". The exception is in the StatsTable.
// TODO(jrg): there is no way to "clean up" all unused named shmem if
// we restart from a crash.  (That isn't a new problem, but it is a problem.)
// In case we want to delete it later, it may be useful to save the value
// of mem_filename after FilePathForMemoryName().
bool SharedMemory::CreateOrOpen(const std::string& name,
                                int posix_flags, uint32 size) {
  DCHECK(mapped_file_ == -1);

  file_util::ScopedFILE file_closer;
  FILE *fp;

  FilePath path;
  if (name.empty()) {
    // It doesn't make sense to have a read-only private piece of shmem
    DCHECK(posix_flags & (O_RDWR | O_WRONLY));

    // Q: Why not use the shm_open() etc. APIs?
    // A: Because they're limited to 4mb on OS X.  FFFFFFFUUUUUUUUUUU
    fp = file_util::CreateAndOpenTemporaryShmemFile(&path);

    // Deleting the file prevents anyone else from mapping it in
    // (making it private), and prevents the need for cleanup (once
    // the last fd is closed, it is truly freed).
    file_util::Delete(path, false);
  } else {
    if (!FilePathForMemoryName(name, &path))
      return false;

    std::string mode;
    switch (posix_flags) {
      case (O_RDWR | O_CREAT):
        // Careful: "w+" will truncate if it already exists.
        mode = "a+";
        break;
      case O_RDWR:
        mode = "r+";
        break;
      case O_RDONLY:
        mode = "r";
        break;
      default:
        NOTIMPLEMENTED();
        break;
    }

    fp = file_util::OpenFile(path, mode.c_str());
  }

  if (fp == NULL) {
    if (posix_flags & O_CREAT) {
#if !defined(OS_MACOSX)
      PLOG(FATAL) << "Creating shared memory in " << path.value() << " failed. "
                  << "This is frequently caused by incorrect permissions on "
                  << "/dev/shm.  Try 'sudo chmod 777 /dev/shm' to fix.";
#else
      PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
#endif
    }
    return false;
  }

  file_closer.reset(fp);  // close when we go out of scope

  // Make sure the (new) file is the right size.
  if (size && (posix_flags & (O_RDWR | O_CREAT))) {
    // Get current size.
    struct stat stat;
    if (fstat(fileno(fp), &stat) != 0)
      return false;
    const uint32 current_size = stat.st_size;
    if (current_size != size) {
      if (ftruncate(fileno(fp), size) != 0)
        return false;
      if (fseeko(fp, size, SEEK_SET) != 0)
        return false;
    }
  }

  mapped_file_ = dup(fileno(fp));
  if (mapped_file_ == -1) {
    if (errno == EMFILE) {
      LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
      return false;
    } else {
      NOTREACHED() << "Call to dup failed, errno=" << errno;
    }
  }

  struct stat st;
  if (fstat(mapped_file_, &st))
    NOTREACHED();
  inode_ = st.st_ino;

  return true;
}

bool SharedMemory::Map(uint32 bytes) {
  if (mapped_file_ == -1)
    return false;

  memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
                 MAP_SHARED, mapped_file_, 0);

  if (memory_)
    max_size_ = bytes;

  bool mmap_succeeded = (memory_ != (void*)-1);
  DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
  return mmap_succeeded;
}

bool SharedMemory::Unmap() {
  if (memory_ == NULL)
    return false;

  munmap(memory_, max_size_);
  memory_ = NULL;
  max_size_ = 0;
  return true;
}

bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
                                        SharedMemoryHandle *new_handle,
                                        bool close_self) {
  const int new_fd = dup(mapped_file_);
  DCHECK(new_fd >= 0);
  new_handle->fd = new_fd;
  new_handle->auto_close = true;

  if (close_self)
    Close();

  return true;
}

void SharedMemory::Close() {
  Unmap();

  if (mapped_file_ > 0) {
    close(mapped_file_);
    mapped_file_ = -1;
  }
}

void SharedMemory::LockOrUnlockCommon(int function) {
  DCHECK(mapped_file_ >= 0);
  while (lockf(mapped_file_, function, 0) < 0) {
    if (errno == EINTR) {
      continue;
    } else if (errno == ENOLCK) {
      // temporary kernel resource exaustion
      PlatformThread::Sleep(500);
      continue;
    } else {
      NOTREACHED() << "lockf() failed."
                   << " function:" << function
                   << " fd:" << mapped_file_
                   << " errno:" << errno
                   << " msg:" << safe_strerror(errno);
    }
  }
}

void SharedMemory::Lock() {
  LockOrUnlockCommon(F_LOCK);
}

void SharedMemory::Unlock() {
  LockOrUnlockCommon(F_ULOCK);
}

SharedMemoryHandle SharedMemory::handle() const {
  return FileDescriptor(mapped_file_, false);
}

}  // namespace base
<commit_msg>shared_memory: improve error messages on failure.<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/shared_memory.h"

#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include "base/file_util.h"
#include "base/logging.h"
#include "base/platform_thread.h"
#include "base/safe_strerror_posix.h"
#include "base/utf_string_conversions.h"

namespace base {

namespace {
// Paranoia. Semaphores and shared memory segments should live in different
// namespaces, but who knows what's out there.
const char kSemaphoreSuffix[] = "-sem";
}

SharedMemory::SharedMemory()
    : mapped_file_(-1),
      inode_(0),
      memory_(NULL),
      read_only_(false),
      max_size_(0) {
}

SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
    : mapped_file_(handle.fd),
      inode_(0),
      memory_(NULL),
      read_only_(read_only),
      max_size_(0) {
  struct stat st;
  if (fstat(handle.fd, &st) == 0) {
    // If fstat fails, then the file descriptor is invalid and we'll learn this
    // fact when Map() fails.
    inode_ = st.st_ino;
  }
}

SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
                           ProcessHandle process)
    : mapped_file_(handle.fd),
      memory_(NULL),
      read_only_(read_only),
      max_size_(0) {
  // We don't handle this case yet (note the ignored parameter); let's die if
  // someone comes calling.
  NOTREACHED();
}

SharedMemory::~SharedMemory() {
  Close();
}

// static
bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
  return handle.fd >= 0;
}

// static
SharedMemoryHandle SharedMemory::NULLHandle() {
  return SharedMemoryHandle();
}

// static
void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
  DCHECK(handle.fd >= 0);
  close(handle.fd);
}

bool SharedMemory::Create(const std::string& name, bool read_only,
                          bool open_existing, uint32 size) {
  read_only_ = read_only;

  int posix_flags = 0;
  posix_flags |= read_only ? O_RDONLY : O_RDWR;
  if (!open_existing || mapped_file_ <= 0)
    posix_flags |= O_CREAT;

  if (!CreateOrOpen(name, posix_flags, size))
    return false;

  max_size_ = size;
  return true;
}

// Our current implementation of shmem is with mmap()ing of files.
// These files need to be deleted explicitly.
// In practice this call is only needed for unit tests.
bool SharedMemory::Delete(const std::string& name) {
  FilePath path;
  if (!FilePathForMemoryName(name, &path))
    return false;

  if (file_util::PathExists(path)) {
    return file_util::Delete(path, false);
  }

  // Doesn't exist, so success.
  return true;
}

bool SharedMemory::Open(const std::string& name, bool read_only) {
  read_only_ = read_only;

  int posix_flags = 0;
  posix_flags |= read_only ? O_RDONLY : O_RDWR;

  return CreateOrOpen(name, posix_flags, 0);
}

// For the given shmem named |mem_name|, return a filename to mmap()
// (and possibly create).  Modifies |filename|.  Return false on
// error, or true of we are happy.
bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
                                         FilePath* path) {
  // mem_name will be used for a filename; make sure it doesn't
  // contain anything which will confuse us.
  DCHECK(mem_name.find('/') == std::string::npos);
  DCHECK(mem_name.find('\0') == std::string::npos);

  FilePath temp_dir;
  if (!file_util::GetShmemTempDir(&temp_dir))
    return false;

  *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name);
  return true;
}

// Chromium mostly only use the unique/private shmem as specified by
// "name == L"". The exception is in the StatsTable.
// TODO(jrg): there is no way to "clean up" all unused named shmem if
// we restart from a crash.  (That isn't a new problem, but it is a problem.)
// In case we want to delete it later, it may be useful to save the value
// of mem_filename after FilePathForMemoryName().
bool SharedMemory::CreateOrOpen(const std::string& name,
                                int posix_flags, uint32 size) {
  DCHECK(mapped_file_ == -1);

  file_util::ScopedFILE file_closer;
  FILE *fp;

  FilePath path;
  if (name.empty()) {
    // It doesn't make sense to have a read-only private piece of shmem
    DCHECK(posix_flags & (O_RDWR | O_WRONLY));

    // Q: Why not use the shm_open() etc. APIs?
    // A: Because they're limited to 4mb on OS X.  FFFFFFFUUUUUUUUUUU
    fp = file_util::CreateAndOpenTemporaryShmemFile(&path);

    // Deleting the file prevents anyone else from mapping it in
    // (making it private), and prevents the need for cleanup (once
    // the last fd is closed, it is truly freed).
    if (fp)
      file_util::Delete(path, false);
  } else {
    if (!FilePathForMemoryName(name, &path))
      return false;

    std::string mode;
    switch (posix_flags) {
      case (O_RDWR | O_CREAT):
        // Careful: "w+" will truncate if it already exists.
        mode = "a+";
        break;
      case O_RDWR:
        mode = "r+";
        break;
      case O_RDONLY:
        mode = "r";
        break;
      default:
        NOTIMPLEMENTED();
        break;
    }

    fp = file_util::OpenFile(path, mode.c_str());
  }

  if (fp == NULL) {
    if (posix_flags & O_CREAT) {
#if !defined(OS_MACOSX)
      PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
      FilePath dir = path.DirName();
      if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
        PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
        if (dir.value() == "/dev/shm") {
          LOG(FATAL) << "This is frequently caused by incorrect permissions on "
                     << "/dev/shm.  Try 'sudo chmod 777 /dev/shm' to fix.";
        }
      }
#else
      PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
#endif
    }
    return false;
  }

  file_closer.reset(fp);  // close when we go out of scope

  // Make sure the (new) file is the right size.
  if (size && (posix_flags & (O_RDWR | O_CREAT))) {
    // Get current size.
    struct stat stat;
    if (fstat(fileno(fp), &stat) != 0)
      return false;
    const uint32 current_size = stat.st_size;
    if (current_size != size) {
      if (ftruncate(fileno(fp), size) != 0)
        return false;
      if (fseeko(fp, size, SEEK_SET) != 0)
        return false;
    }
  }

  mapped_file_ = dup(fileno(fp));
  if (mapped_file_ == -1) {
    if (errno == EMFILE) {
      LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
      return false;
    } else {
      NOTREACHED() << "Call to dup failed, errno=" << errno;
    }
  }

  struct stat st;
  if (fstat(mapped_file_, &st))
    NOTREACHED();
  inode_ = st.st_ino;

  return true;
}

bool SharedMemory::Map(uint32 bytes) {
  if (mapped_file_ == -1)
    return false;

  memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
                 MAP_SHARED, mapped_file_, 0);

  if (memory_)
    max_size_ = bytes;

  bool mmap_succeeded = (memory_ != (void*)-1);
  DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno;
  return mmap_succeeded;
}

bool SharedMemory::Unmap() {
  if (memory_ == NULL)
    return false;

  munmap(memory_, max_size_);
  memory_ = NULL;
  max_size_ = 0;
  return true;
}

bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
                                        SharedMemoryHandle *new_handle,
                                        bool close_self) {
  const int new_fd = dup(mapped_file_);
  DCHECK(new_fd >= 0);
  new_handle->fd = new_fd;
  new_handle->auto_close = true;

  if (close_self)
    Close();

  return true;
}

void SharedMemory::Close() {
  Unmap();

  if (mapped_file_ > 0) {
    close(mapped_file_);
    mapped_file_ = -1;
  }
}

void SharedMemory::LockOrUnlockCommon(int function) {
  DCHECK(mapped_file_ >= 0);
  while (lockf(mapped_file_, function, 0) < 0) {
    if (errno == EINTR) {
      continue;
    } else if (errno == ENOLCK) {
      // temporary kernel resource exaustion
      PlatformThread::Sleep(500);
      continue;
    } else {
      NOTREACHED() << "lockf() failed."
                   << " function:" << function
                   << " fd:" << mapped_file_
                   << " errno:" << errno
                   << " msg:" << safe_strerror(errno);
    }
  }
}

void SharedMemory::Lock() {
  LockOrUnlockCommon(F_LOCK);
}

void SharedMemory::Unlock() {
  LockOrUnlockCommon(F_ULOCK);
}

SharedMemoryHandle SharedMemory::handle() const {
  return FileDescriptor(mapped_file_, false);
}

}  // namespace base
<|endoftext|>


<commit_before>
#include "journal/player.hpp"

#include <list>
#include <string>
#include <vector>

#include "benchmark/benchmark.h"
#include "glog/logging.h"
#include "gtest/gtest.h"
#include "journal/method.hpp"
#include "journal/profiles.hpp"
#include "journal/recorder.hpp"
#include "ksp_plugin/interface.hpp"
#include "serialization/journal.pb.h"

namespace principia {
namespace journal {

void BM_PlayForReal(benchmark::State& state) {
  while (state.KeepRunning()) {
    Player player(
        R"(P:\Public Mockingbird\Principia\Journals\JOURNAL.20180311-192733)");
    int count = 0;
    while (player.Play(count)) {
      ++count;
      LOG_IF(ERROR, (count % 100'000) == 0)
          << count << " journal entries replayed";
    }
  }
}

BENCHMARK(BM_PlayForReal);

class PlayerTest : public ::testing::Test {
 protected:
  PlayerTest()
      : test_info_(testing::UnitTest::GetInstance()->current_test_info()),
        test_case_name_(test_info_->test_case_name()),
        test_name_(test_info_->name()),
        plugin_(interface::principia__NewPlugin("MJD0", "MJD0", 0)) {}

  template<typename Profile>
  bool RunIfAppropriate(serialization::Method const& method_in,
                        serialization::Method const& method_out_return,
                        Player& player) {
    return player.RunIfAppropriate<Profile>(method_in, method_out_return);
  }

  ::testing::TestInfo const* const test_info_;
  std::string const test_case_name_;
  std::string const test_name_;
  std::unique_ptr<ksp_plugin::Plugin> plugin_;
};

TEST_F(PlayerTest, PlayTiny) {
  {
    Recorder* const r(new Recorder(test_name_ + ".journal.hex"));
    Recorder::Activate(r);

    {
      Method<NewPlugin> m({"MJD1", "MJD2", 3});
      m.Return(plugin_.get());
    }
    {
      const ksp_plugin::Plugin* plugin = plugin_.get();
      Method<DeletePlugin> m({&plugin}, {&plugin});
      m.Return();
    }
    Recorder::Deactivate();
  }

  Player player(test_name_ + ".journal.hex");

  // Replay the journal.
  int count = 0;
  while (player.Play(count)) {
    ++count;
  }
  EXPECT_EQ(2, count);
}

TEST_F(PlayerTest, DISABLED_Benchmarks) {
  benchmark::RunSpecifiedBenchmarks();
}

TEST_F(PlayerTest, DISABLED_SECULAR_Debug) {
  // An example of how journaling may be used for debugging.  You must set
  // |path| and fill the |method_in| and |method_out_return| protocol buffers.
  std::string path =
      R"(C:\Program Files\Kerbal Space Program\1.7.0\glog\Principia\JOURNAL.20190713-140753)";  // NOLINT
  Player player(path);
  int count = 0;
  while (player.Play(count)) {
    ++count;
    LOG_IF(ERROR, (count % 100'000) == 0) << count
                                          << " journal entries replayed";
  }
  LOG(ERROR) << count << " journal entries in total";
  LOG(ERROR) << "Last successful method in:\n"
             << player.last_method_in().DebugString();
  LOG(ERROR) << "Last successful method out/return: \n"
             << player.last_method_out_return().DebugString();

#if 0
  serialization::Method method_in;
  {
    auto* extension = method_in.MutableExtension(
        serialization::FlightPlanGetManoeuvreFrenetTrihedron::extension);
    auto* in = extension->mutable_in();
    in->set_plugin(2312193072);
    in->set_vessel_guid("bedd9d23-de56-4fb8-881c-f647e22f848f");
    in->set_index(0);
  }
  serialization::Method method_out_return;
  {
    auto* extension = method_out_return.MutableExtension(
        serialization::FlightPlanGetManoeuvreFrenetTrihedron::extension);
  }
  LOG(ERROR) << "Running unpaired method:\n" << method_in.DebugString();
  CHECK(RunIfAppropriate<FlightPlanGetManoeuvreFrenetTrihedron>(
      method_in, method_out_return, player));
#endif
}

}  // namespace journal
}  // namespace principia
<commit_msg>mark PlayerTest.*Benchmarks as secular<commit_after>
#include "journal/player.hpp"

#include <list>
#include <string>
#include <vector>

#include "benchmark/benchmark.h"
#include "glog/logging.h"
#include "gtest/gtest.h"
#include "journal/method.hpp"
#include "journal/profiles.hpp"
#include "journal/recorder.hpp"
#include "ksp_plugin/interface.hpp"
#include "serialization/journal.pb.h"

namespace principia {
namespace journal {

void BM_PlayForReal(benchmark::State& state) {
  while (state.KeepRunning()) {
    Player player(
        R"(P:\Public Mockingbird\Principia\Journals\JOURNAL.20180311-192733)");
    int count = 0;
    while (player.Play(count)) {
      ++count;
      LOG_IF(ERROR, (count % 100'000) == 0)
          << count << " journal entries replayed";
    }
  }
}

BENCHMARK(BM_PlayForReal);

class PlayerTest : public ::testing::Test {
 protected:
  PlayerTest()
      : test_info_(testing::UnitTest::GetInstance()->current_test_info()),
        test_case_name_(test_info_->test_case_name()),
        test_name_(test_info_->name()),
        plugin_(interface::principia__NewPlugin("MJD0", "MJD0", 0)) {}

  template<typename Profile>
  bool RunIfAppropriate(serialization::Method const& method_in,
                        serialization::Method const& method_out_return,
                        Player& player) {
    return player.RunIfAppropriate<Profile>(method_in, method_out_return);
  }

  ::testing::TestInfo const* const test_info_;
  std::string const test_case_name_;
  std::string const test_name_;
  std::unique_ptr<ksp_plugin::Plugin> plugin_;
};

TEST_F(PlayerTest, PlayTiny) {
  {
    Recorder* const r(new Recorder(test_name_ + ".journal.hex"));
    Recorder::Activate(r);

    {
      Method<NewPlugin> m({"MJD1", "MJD2", 3});
      m.Return(plugin_.get());
    }
    {
      const ksp_plugin::Plugin* plugin = plugin_.get();
      Method<DeletePlugin> m({&plugin}, {&plugin});
      m.Return();
    }
    Recorder::Deactivate();
  }

  Player player(test_name_ + ".journal.hex");

  // Replay the journal.
  int count = 0;
  while (player.Play(count)) {
    ++count;
  }
  EXPECT_EQ(2, count);
}

TEST_F(PlayerTest, DISABLED_SECULAR_Benchmarks) {
  benchmark::RunSpecifiedBenchmarks();
}

TEST_F(PlayerTest, DISABLED_SECULAR_Debug) {
  // An example of how journaling may be used for debugging.  You must set
  // |path| and fill the |method_in| and |method_out_return| protocol buffers.
  std::string path =
      R"(C:\Program Files\Kerbal Space Program\1.7.0\glog\Principia\JOURNAL.20190713-140753)";  // NOLINT
  Player player(path);
  int count = 0;
  while (player.Play(count)) {
    ++count;
    LOG_IF(ERROR, (count % 100'000) == 0) << count
                                          << " journal entries replayed";
  }
  LOG(ERROR) << count << " journal entries in total";
  LOG(ERROR) << "Last successful method in:\n"
             << player.last_method_in().DebugString();
  LOG(ERROR) << "Last successful method out/return: \n"
             << player.last_method_out_return().DebugString();

#if 0
  serialization::Method method_in;
  {
    auto* extension = method_in.MutableExtension(
        serialization::FlightPlanGetManoeuvreFrenetTrihedron::extension);
    auto* in = extension->mutable_in();
    in->set_plugin(2312193072);
    in->set_vessel_guid("bedd9d23-de56-4fb8-881c-f647e22f848f");
    in->set_index(0);
  }
  serialization::Method method_out_return;
  {
    auto* extension = method_out_return.MutableExtension(
        serialization::FlightPlanGetManoeuvreFrenetTrihedron::extension);
  }
  LOG(ERROR) << "Running unpaired method:\n" << method_in.DebugString();
  CHECK(RunIfAppropriate<FlightPlanGetManoeuvreFrenetTrihedron>(
      method_in, method_out_return, player));
#endif
}

}  // namespace journal
}  // namespace principia
<|endoftext|>