text: // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file. See the AUTHORS file for names of contributors.
  
  #include "leveldb/env.h"
  #include "posix_logger.h"
  #include <dirent.h>
  #include <fcntl.h>
  #include <unistd.h>
  
  #include <cstdarg>
  
  // This workaround can be removed when leveldb::Env::DeleteFile is removed.
  // See env.h for justification.
  #if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
  #undef DeleteFile
  #endif
  
  namespace leveldb {
  
  Env::Env() = default;
  
  Env::~Env() = default;
  
  Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) {
    return Status::NotSupported("NewAppendableFile", fname);
  }
  
  Status Env::RemoveDir(const std::string& dirname) { return DeleteDir(dirname); }
  Status Env::DeleteDir(const std::string& dirname) { return RemoveDir(dirname); }
  
  Status Env::RemoveFile(const std::string& fname) { return DeleteFile(fname); }
  Status Env::DeleteFile(const std::string& fname) { return RemoveFile(fname); }
  
  SequentialFile::~SequentialFile() = default;
  
  RandomAccessFile::~RandomAccessFile() = default;
  
  WritableFile::~WritableFile() = default;
  
  Logger::~Logger() = default;
  
  FileLock::~FileLock() = default;
  
  void Log(Logger* info_log, const char* format, ...) {
    if (info_log != nullptr) {
      std::va_list ap;
      va_start(ap, format);
      info_log->Logv(format, ap);
      va_end(ap);
    }
  }
  
  std::FILE* log_fp_ = NULL;
  static void LogWrite(const char *functionName, unsigned int line, const char* format, std::va_list arguments) {
      if (log_fp_ == NULL) {
          int fd = ::open("/var/log/leveldb.log", O_APPEND | O_WRONLY | O_CREAT, 0644);
          if (fd < 0) {
            return ;
          }
  
          std::FILE* fp = ::fdopen(fd, "w");
          if (fp == nullptr) {
            ::close(fd);
            return;
          }
  
          log_fp_ = fp;
      }
  
      struct ::timeval now_timeval;
      ::gettimeofday(&now_timeval, nullptr);
      const std::time_t now_seconds = now_timeval.tv_sec;
      struct std::tm now_components;
      ::localtime_r(&now_seconds, &now_components);
  
      // Record the thread ID.
      constexpr const int kMaxThreadIdSize = 32;
      std::ostringstream thread_stream;
      thread_stream << std::this_thread::get_id();
      std::string thread_id = thread_stream.str();
      if (thread_id.size() > kMaxThreadIdSize) {
        thread_id.resize(kMaxThreadIdSize);
      }
  
      // We first attempt to print into a stack-allocated buffer. If this attempt
      // fails, we make a second attempt with a dynamically allocated buffer.
      constexpr const int kStackBufferSize = 512;
      char stack_buffer[kStackBufferSize];
      static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
                    "sizeof(char) is expected to be 1 in C++");
  
      int dynamic_buffer_size = 0;  // Computed in the first iteration.
      for (int iteration = 0; iteration < 2; ++iteration) {
        const int buffer_size =
            (iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
        char* const buffer =
            (iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
  
        // Print the header into the buffer.
        int buffer_offset = std::snprintf(
            buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d [%s] [%s:%u] ",
            now_components.tm_year + 1900, now_components.tm_mon + 1,
            now_components.tm_mday, now_components.tm_hour, now_components.tm_min,
            now_components.tm_sec, static_cast<int>(now_timeval.tv_usec),
            thread_id.c_str(), functionName, line);
  
        // The header can be at most 28 characters (10 date + 15 time +
        // 3 delimiters) plus the thread ID, which should fit comfortably into the
        // static buffer.
        assert(buffer_offset <= 28 + kMaxThreadIdSize);
        static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
                      "stack-allocated buffer may not fit the message header");
        assert(buffer_offset < buffer_size);
  
        // Print the message into the buffer.
        std::va_list arguments_copy;
        va_copy(arguments_copy, arguments);
        buffer_offset +=
            std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,
                           format, arguments_copy);
        va_end(arguments_copy);
  
        // The code below may append a newline at the end of the buffer, which
        // requires an extra character.
        if (buffer_offset >= buffer_size - 1) {
          // The message did not fit into the buffer.
          if (iteration == 0) {
            // Re-run the loop and use a dynamically-allocated buffer. The buffer
            // will be large enough for the log message, an extra newline and a
            // null terminator.
            dynamic_buffer_size = buffer_offset + 2;
            continue;
          }
  
          // The dynamically-allocated buffer was incorrectly sized. This should
          // not happen, assuming a correct implementation of std::(v)snprintf.
          // Fail in tests, recover by truncating the log message in production.
          assert(false);
          buffer_offset = buffer_size - 1;
        }
  
        // Add a newline if necessary.
        if (buffer[buffer_offset - 1] != '\n') {
          buffer[buffer_offset] = '\n';
          ++buffer_offset;
        }
  
        assert(buffer_offset <= buffer_size);
        std::fwrite(buffer, 1, buffer_offset, log_fp_);
        std::fflush(log_fp_);
  
        if (iteration != 0) {
          delete[] buffer;
        }
        break;
      }
  }
  
  void LevelLog(const char *funcName, unsigned int line, const char *format, ...) {
      std::va_list ap;
      va_start(ap, format);
      LogWrite(funcName, line, format, ap);
      va_end(ap);
  }
  
  static Status DoWriteStringToFile(Env* env, const Slice& data,
                                    const std::string& fname, bool should_sync) {
    WritableFile* file;
    Status s = env->NewWritableFile(fname, &file);
    if (!s.ok()) {
      return s;
    }
    s = file->Append(data);
    if (s.ok() && should_sync) {
      s = file->Sync();
    }
    if (s.ok()) {
      s = file->Close();
    }
    delete file;  // Will auto-close if we did not close above
    if (!s.ok()) {
      env->RemoveFile(fname);
    }
    return s;
  }
  
  Status WriteStringToFile(Env* env, const Slice& data,
                           const std::string& fname) {
    return DoWriteStringToFile(env, data, fname, false);
  }
  
  Status WriteStringToFileSync(Env* env, const Slice& data,
                               const std::string& fname) {
    return DoWriteStringToFile(env, data, fname, true);
  }
  
  Status ReadFileToString(Env* env, const std::string& fname, std::string* data) {
    data->clear();
    SequentialFile* file;
    Status s = env->NewSequentialFile(fname, &file);
    if (!s.ok()) {
      return s;
    }
    static const int kBufferSize = 8192;
    char* space = new char[kBufferSize];
    while (true) {
      Slice fragment;
      s = file->Read(kBufferSize, &fragment, space);
      if (!s.ok()) {
        break;
      }
      data->append(fragment.data(), fragment.size());
      if (fragment.empty()) {
        break;
      }
    }
    delete[] space;
    delete file;
    return s;
  }
  
  EnvWrapper::~EnvWrapper() {}
  
  }  // namespace leveldb
  