<commit_before>// Copyright 2022 Google LLC
//
// 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.

// Make `fseeko()` and `ftello()` available.
#if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 500
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif

// Make `off_t` 64-bit even on 32-bit systems.
#undef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64

#include "riegeli/bytes/cfile_writer.h"

#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>

#include <cerrno>
#include <limits>
#include <string>
#include <utility>

#include "absl/base/optimization.h"
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "riegeli/base/arithmetic.h"
#include "riegeli/base/assert.h"
#include "riegeli/base/errno_mapping.h"
#include "riegeli/base/object.h"
#include "riegeli/base/status.h"
#include "riegeli/base/types.h"
#include "riegeli/bytes/buffered_writer.h"
#include "riegeli/bytes/cfile_internal.h"
#include "riegeli/bytes/cfile_reader.h"
#include "riegeli/bytes/reader.h"
#include "riegeli/bytes/writer.h"

namespace riegeli {

void CFileWriterBase::Initialize(FILE* dest, std::string&& assumed_filename,
                                 absl::optional<Position> assumed_pos,
                                 bool append) {
  RIEGELI_ASSERT(dest != nullptr)
      << "Failed precondition of CFileReader: null FILE pointer";
  filename_ = std::move(assumed_filename);
  InitializePos(dest, assumed_pos, append);
}

FILE* CFileWriterBase::OpenFile(absl::string_view filename, const char* mode) {
  // TODO: When `absl::string_view` becomes C++17 `std::string_view`:
  // `filename_ = filename`
  filename_.assign(filename.data(), filename.size());
  FILE* const dest = fopen(filename_.c_str(), mode);
  if (ABSL_PREDICT_FALSE(dest == nullptr)) {
    BufferedWriter::Reset(kClosed);
    FailOperation("fopen()");
    return nullptr;
  }
  return dest;
}

inline void CFileWriterBase::InitializePos(FILE* dest,
                                           absl::optional<Position> assumed_pos,
                                           bool append) {
  RIEGELI_ASSERT(supports_random_access_ == LazyBoolState::kFalse)
      << "Failed precondition of CFileWriterBase::InitializePos(): "
         "supports_random_access_ not reset";
  RIEGELI_ASSERT(supports_read_mode_ == LazyBoolState::kFalse)
      << "Failed precondition of CFileWriterBase::InitializePos(): "
         "supports_read_mode_ not reset";
  if (ABSL_PREDICT_FALSE(ferror(dest))) {
    FailOperation("FILE");
    return;
  }
  if (assumed_pos != absl::nullopt) {
    if (ABSL_PREDICT_FALSE(
            *assumed_pos >
            Position{std::numeric_limits<cfile_internal::Offset>::max()})) {
      FailOverflow();
      return;
    }
    set_start_pos(*assumed_pos);
  } else {
    if (append) {
      if (cfile_internal::FSeek(dest, 0, SEEK_END) != 0) {
        // Random access is not supported. Assume the current position as 0.
        clearerr(dest);
        return;
      }
    }
    const cfile_internal::Offset file_pos = cfile_internal::FTell(dest);
    if (file_pos < 0) {
      // Random access is not supported. Assume the current position as 0.
      clearerr(dest);
      return;
    }
    set_start_pos(IntCast<Position>(file_pos));
    if (append) {
      // Random access is not supported because writing in append mode always
      // happens at the end. `supports_random_access_` is left as
      // `LazyBoolState::kFalse`.
    } else {
      // `ftell()` succeeded, and `fseek(SEEK_END)` will be checked later.
      supports_random_access_ = LazyBoolState::kUnknown;
    }
    supports_read_mode_ = LazyBoolState::kUnknown;
  }
  BeginRun();
}

void CFileWriterBase::Done() {
  BufferedWriter::Done();
  associated_reader_.Reset();
}

bool CFileWriterBase::FailOperation(absl::string_view operation) {
  const int error_number = errno;
  RIEGELI_ASSERT_NE(error_number, 0)
      << "Failed precondition of CFileWriterBase::FailOperation(): "
         "zero errno";
  return Fail(ErrnoToStatus(error_number, absl::StrCat(operation, " failed")));
}

absl::Status CFileWriterBase::AnnotateStatusImpl(absl::Status status) {
  if (!filename_.empty()) {
    status = Annotate(status, absl::StrCat("writing ", filename_));
  }
  return BufferedWriter::AnnotateStatusImpl(std::move(status));
}

bool CFileWriterBase::supports_random_access() {
  switch (supports_random_access_) {
    case LazyBoolState::kFalse:
      return false;
    case LazyBoolState::kTrue:
      return true;
    case LazyBoolState::kUnknown:
      break;
  }
  bool supported = false;
  if (ABSL_PREDICT_TRUE(is_open())) {
    if (ABSL_PREDICT_FALSE(absl::StartsWith(filename(), "/sys/"))) {
      // "/sys" files do not support random access. It is hard to reliably
      // recognize them, so `CFileWriter` checks the filename.
    } else {
      FILE* const dest = DestFile();
      if (cfile_internal::FSeek(dest, 0, SEEK_END) != 0) {
        // Not supported.
        clearerr(dest);
      } else {
        const cfile_internal::Offset file_size = cfile_internal::FTell(dest);
        if (ABSL_PREDICT_FALSE(file_size < 0)) {
          FailOperation(cfile_internal::kFTellFunctionName);
        } else if (ABSL_PREDICT_FALSE(
                       cfile_internal::FSeek(
                           dest, IntCast<cfile_internal::Offset>(limit_pos()),
                           SEEK_SET) != 0)) {
          FailOperation(cfile_internal::kFSeekFunctionName);
        } else if (file_size == 0 &&
                   ABSL_PREDICT_FALSE(absl::StartsWith(filename(), "/proc/"))) {
          // Some "/proc" files do not support random access. It is hard to
          // reliably recognize them using the `FILE` API, so `CFileWriter`
          // checks the filename. Random access is assumed to be unsupported if
          // they claim to have a zero size.
        } else {
          supported = true;
        }
      }
    }
  }
  supports_random_access_ =
      supported ? LazyBoolState::kTrue : LazyBoolState::kFalse;
  return supported;
}

bool CFileWriterBase::supports_read_mode() {
  switch (supports_read_mode_) {
    case LazyBoolState::kFalse:
      return false;
    case LazyBoolState::kTrue:
      return true;
    case LazyBoolState::kUnknown:
      break;
  }
  bool supported = false;
  if (supports_random_access() && ABSL_PREDICT_TRUE(is_open())) {
    FILE* const dest = DestFile();
    if (cfile_internal::FSeek(dest, 0, SEEK_END) != 0) {
      clearerr(dest);
    } else {
      char buf[1];
      fread(buf, 1, 1, dest);
      if (!ferror(dest)) supported = true;
      clearerr(dest);
      if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(
                                 dest,
                                 IntCast<cfile_internal::Offset>(start_pos()),
                                 SEEK_SET) != 0)) {
        return FailOperation(cfile_internal::kFSeekFunctionName);
      }
    }
  }
  supports_read_mode_ =
      supported ? LazyBoolState::kTrue : LazyBoolState::kFalse;
  return supported;
}

inline bool CFileWriterBase::WriteMode() {
  if (ABSL_PREDICT_TRUE(!read_mode_)) return true;
  read_mode_ = false;
  if (ABSL_PREDICT_FALSE(!ok())) return false;
  FILE* const dest = DestFile();
  if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(
                             dest, IntCast<cfile_internal::Offset>(start_pos()),
                             SEEK_SET) != 0)) {
    return FailOperation(cfile_internal::kFSeekFunctionName);
  }
  return true;
}

bool CFileWriterBase::WriteInternal(absl::string_view src) {
  RIEGELI_ASSERT(!src.empty())
      << "Failed precondition of BufferedWriter::WriteInternal(): "
         "nothing to write";
  RIEGELI_ASSERT(ok())
      << "Failed precondition of BufferedWriter::WriteInternal(): " << status();
  if (ABSL_PREDICT_FALSE(!WriteMode())) return false;
  FILE* const dest = DestFile();
  if (ABSL_PREDICT_FALSE(
          src.size() >
          Position{std::numeric_limits<cfile_internal::Offset>::max()} -
              start_pos())) {
    return FailOverflow();
  }
  const size_t length_written = fwrite(src.data(), 1, src.size(), dest);
  RIEGELI_ASSERT_LE(length_written, src.size())
      << "fwrite() wrote more than requested";
  move_start_pos(length_written);
  if (ABSL_PREDICT_FALSE(length_written < src.size())) {
    RIEGELI_ASSERT(ferror(dest))
        << "fwrite() succeeded but wrote less than requested";
    return FailOperation("fwrite()");
  }
  return true;
}

bool CFileWriterBase::FlushBehindBuffer(absl::string_view src,
                                        FlushType flush_type) {
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::FlushBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!WriteMode())) return false;
  return BufferedWriter::FlushBehindBuffer(src, flush_type);
}

bool CFileWriterBase::SeekBehindBuffer(Position new_pos) {
  RIEGELI_ASSERT_NE(new_pos, pos())
      << "Failed precondition of BufferedWriter::SeekBehindBuffer(): "
         "position unchanged, use Seek() instead";
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::SeekBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!supports_random_access())) {
    // Delegate to base class version which fails, to avoid duplicating the
    // failure message here.
    return BufferedWriter::SeekBehindBuffer(new_pos);
  }
  if (ABSL_PREDICT_FALSE(!ok())) return false;
  read_mode_ = false;
  FILE* const dest = DestFile();
  if (new_pos > start_pos()) {
    // Seeking forwards.
    if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(dest, 0, SEEK_END) != 0)) {
      return FailOperation(cfile_internal::kFSeekFunctionName);
    }
    const cfile_internal::Offset file_size = cfile_internal::FTell(dest);
    if (ABSL_PREDICT_FALSE(file_size < 0)) {
      return FailOperation(cfile_internal::kFTellFunctionName);
    }
    if (ABSL_PREDICT_FALSE(new_pos > IntCast<Position>(file_size))) {
      // Stream ends.
      set_start_pos(IntCast<Position>(file_size));
      return false;
    }
  }
  if (ABSL_PREDICT_FALSE(
          cfile_internal::FSeek(dest, IntCast<cfile_internal::Offset>(new_pos),
                                SEEK_SET) != 0)) {
    return FailOperation(cfile_internal::kFSeekFunctionName);
  }
  set_start_pos(new_pos);
  return true;
}

absl::optional<Position> CFileWriterBase::SizeBehindBuffer() {
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::SizeBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!supports_random_access())) {
    // Delegate to base class version which fails, to avoid duplicating the
    // failure message here.
    return BufferedWriter::SizeBehindBuffer();
  }
  if (ABSL_PREDICT_FALSE(!ok())) return absl::nullopt;
  read_mode_ = false;
  FILE* const dest = DestFile();
  if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(dest, 0, SEEK_END) != 0)) {
    FailOperation(cfile_internal::kFSeekFunctionName);
    return absl::nullopt;
  }
  const cfile_internal::Offset file_size = cfile_internal::FTell(dest);
  if (ABSL_PREDICT_FALSE(file_size < 0)) {
    FailOperation(cfile_internal::kFTellFunctionName);
    return absl::nullopt;
  }
  if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(
                             dest, IntCast<cfile_internal::Offset>(start_pos()),
                             SEEK_SET) != 0)) {
    FailOperation(cfile_internal::kFSeekFunctionName);
    return absl::nullopt;
  }
  return IntCast<Position>(file_size);
}

Reader* CFileWriterBase::ReadModeBehindBuffer(Position initial_pos) {
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::ReadModeBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!supports_read_mode())) {
    // Delegate to base class version which fails, to avoid duplicating the
    // failure message here.
    return BufferedWriter::ReadModeBehindBuffer(initial_pos);
  }
  if (ABSL_PREDICT_FALSE(!ok())) return nullptr;
  FILE* const dest = DestFile();
  CFileReader<UnownedCFile>* const reader = associated_reader_.ResetReader(
      dest, CFileReaderBase::Options()
                .set_assumed_filename(filename())
                .set_buffer_options(buffer_options()));
  read_mode_ = true;
  reader->Seek(initial_pos);
  return reader;
}

}  // namespace riegeli
<commit_msg>Remove wrong `inline` at `CFileWriterBase::InitializePos()` which is called from the header file and thus from other translation units.<commit_after>// Copyright 2022 Google LLC
//
// 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.

// Make `fseeko()` and `ftello()` available.
#if !defined(_XOPEN_SOURCE) || _XOPEN_SOURCE < 500
#undef _XOPEN_SOURCE
#define _XOPEN_SOURCE 500
#endif

// Make `off_t` 64-bit even on 32-bit systems.
#undef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64

#include "riegeli/bytes/cfile_writer.h"

#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>

#include <cerrno>
#include <limits>
#include <string>
#include <utility>

#include "absl/base/optimization.h"
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "riegeli/base/arithmetic.h"
#include "riegeli/base/assert.h"
#include "riegeli/base/errno_mapping.h"
#include "riegeli/base/object.h"
#include "riegeli/base/status.h"
#include "riegeli/base/types.h"
#include "riegeli/bytes/buffered_writer.h"
#include "riegeli/bytes/cfile_internal.h"
#include "riegeli/bytes/cfile_reader.h"
#include "riegeli/bytes/reader.h"
#include "riegeli/bytes/writer.h"

namespace riegeli {

void CFileWriterBase::Initialize(FILE* dest, std::string&& assumed_filename,
                                 absl::optional<Position> assumed_pos,
                                 bool append) {
  RIEGELI_ASSERT(dest != nullptr)
      << "Failed precondition of CFileReader: null FILE pointer";
  filename_ = std::move(assumed_filename);
  InitializePos(dest, assumed_pos, append);
}

FILE* CFileWriterBase::OpenFile(absl::string_view filename, const char* mode) {
  // TODO: When `absl::string_view` becomes C++17 `std::string_view`:
  // `filename_ = filename`
  filename_.assign(filename.data(), filename.size());
  FILE* const dest = fopen(filename_.c_str(), mode);
  if (ABSL_PREDICT_FALSE(dest == nullptr)) {
    BufferedWriter::Reset(kClosed);
    FailOperation("fopen()");
    return nullptr;
  }
  return dest;
}

void CFileWriterBase::InitializePos(FILE* dest,
                                    absl::optional<Position> assumed_pos,
                                    bool append) {
  RIEGELI_ASSERT(supports_random_access_ == LazyBoolState::kFalse)
      << "Failed precondition of CFileWriterBase::InitializePos(): "
         "supports_random_access_ not reset";
  RIEGELI_ASSERT(supports_read_mode_ == LazyBoolState::kFalse)
      << "Failed precondition of CFileWriterBase::InitializePos(): "
         "supports_read_mode_ not reset";
  if (ABSL_PREDICT_FALSE(ferror(dest))) {
    FailOperation("FILE");
    return;
  }
  if (assumed_pos != absl::nullopt) {
    if (ABSL_PREDICT_FALSE(
            *assumed_pos >
            Position{std::numeric_limits<cfile_internal::Offset>::max()})) {
      FailOverflow();
      return;
    }
    set_start_pos(*assumed_pos);
  } else {
    if (append) {
      if (cfile_internal::FSeek(dest, 0, SEEK_END) != 0) {
        // Random access is not supported. Assume the current position as 0.
        clearerr(dest);
        return;
      }
    }
    const cfile_internal::Offset file_pos = cfile_internal::FTell(dest);
    if (file_pos < 0) {
      // Random access is not supported. Assume the current position as 0.
      clearerr(dest);
      return;
    }
    set_start_pos(IntCast<Position>(file_pos));
    if (append) {
      // Random access is not supported because writing in append mode always
      // happens at the end. `supports_random_access_` is left as
      // `LazyBoolState::kFalse`.
    } else {
      // `ftell()` succeeded, and `fseek(SEEK_END)` will be checked later.
      supports_random_access_ = LazyBoolState::kUnknown;
    }
    supports_read_mode_ = LazyBoolState::kUnknown;
  }
  BeginRun();
}

void CFileWriterBase::Done() {
  BufferedWriter::Done();
  associated_reader_.Reset();
}

bool CFileWriterBase::FailOperation(absl::string_view operation) {
  const int error_number = errno;
  RIEGELI_ASSERT_NE(error_number, 0)
      << "Failed precondition of CFileWriterBase::FailOperation(): "
         "zero errno";
  return Fail(ErrnoToStatus(error_number, absl::StrCat(operation, " failed")));
}

absl::Status CFileWriterBase::AnnotateStatusImpl(absl::Status status) {
  if (!filename_.empty()) {
    status = Annotate(status, absl::StrCat("writing ", filename_));
  }
  return BufferedWriter::AnnotateStatusImpl(std::move(status));
}

bool CFileWriterBase::supports_random_access() {
  switch (supports_random_access_) {
    case LazyBoolState::kFalse:
      return false;
    case LazyBoolState::kTrue:
      return true;
    case LazyBoolState::kUnknown:
      break;
  }
  bool supported = false;
  if (ABSL_PREDICT_TRUE(is_open())) {
    if (ABSL_PREDICT_FALSE(absl::StartsWith(filename(), "/sys/"))) {
      // "/sys" files do not support random access. It is hard to reliably
      // recognize them, so `CFileWriter` checks the filename.
    } else {
      FILE* const dest = DestFile();
      if (cfile_internal::FSeek(dest, 0, SEEK_END) != 0) {
        // Not supported.
        clearerr(dest);
      } else {
        const cfile_internal::Offset file_size = cfile_internal::FTell(dest);
        if (ABSL_PREDICT_FALSE(file_size < 0)) {
          FailOperation(cfile_internal::kFTellFunctionName);
        } else if (ABSL_PREDICT_FALSE(
                       cfile_internal::FSeek(
                           dest, IntCast<cfile_internal::Offset>(limit_pos()),
                           SEEK_SET) != 0)) {
          FailOperation(cfile_internal::kFSeekFunctionName);
        } else if (file_size == 0 &&
                   ABSL_PREDICT_FALSE(absl::StartsWith(filename(), "/proc/"))) {
          // Some "/proc" files do not support random access. It is hard to
          // reliably recognize them using the `FILE` API, so `CFileWriter`
          // checks the filename. Random access is assumed to be unsupported if
          // they claim to have a zero size.
        } else {
          supported = true;
        }
      }
    }
  }
  supports_random_access_ =
      supported ? LazyBoolState::kTrue : LazyBoolState::kFalse;
  return supported;
}

bool CFileWriterBase::supports_read_mode() {
  switch (supports_read_mode_) {
    case LazyBoolState::kFalse:
      return false;
    case LazyBoolState::kTrue:
      return true;
    case LazyBoolState::kUnknown:
      break;
  }
  bool supported = false;
  if (supports_random_access() && ABSL_PREDICT_TRUE(is_open())) {
    FILE* const dest = DestFile();
    if (cfile_internal::FSeek(dest, 0, SEEK_END) != 0) {
      clearerr(dest);
    } else {
      char buf[1];
      fread(buf, 1, 1, dest);
      if (!ferror(dest)) supported = true;
      clearerr(dest);
      if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(
                                 dest,
                                 IntCast<cfile_internal::Offset>(start_pos()),
                                 SEEK_SET) != 0)) {
        return FailOperation(cfile_internal::kFSeekFunctionName);
      }
    }
  }
  supports_read_mode_ =
      supported ? LazyBoolState::kTrue : LazyBoolState::kFalse;
  return supported;
}

inline bool CFileWriterBase::WriteMode() {
  if (ABSL_PREDICT_TRUE(!read_mode_)) return true;
  read_mode_ = false;
  if (ABSL_PREDICT_FALSE(!ok())) return false;
  FILE* const dest = DestFile();
  if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(
                             dest, IntCast<cfile_internal::Offset>(start_pos()),
                             SEEK_SET) != 0)) {
    return FailOperation(cfile_internal::kFSeekFunctionName);
  }
  return true;
}

bool CFileWriterBase::WriteInternal(absl::string_view src) {
  RIEGELI_ASSERT(!src.empty())
      << "Failed precondition of BufferedWriter::WriteInternal(): "
         "nothing to write";
  RIEGELI_ASSERT(ok())
      << "Failed precondition of BufferedWriter::WriteInternal(): " << status();
  if (ABSL_PREDICT_FALSE(!WriteMode())) return false;
  FILE* const dest = DestFile();
  if (ABSL_PREDICT_FALSE(
          src.size() >
          Position{std::numeric_limits<cfile_internal::Offset>::max()} -
              start_pos())) {
    return FailOverflow();
  }
  const size_t length_written = fwrite(src.data(), 1, src.size(), dest);
  RIEGELI_ASSERT_LE(length_written, src.size())
      << "fwrite() wrote more than requested";
  move_start_pos(length_written);
  if (ABSL_PREDICT_FALSE(length_written < src.size())) {
    RIEGELI_ASSERT(ferror(dest))
        << "fwrite() succeeded but wrote less than requested";
    return FailOperation("fwrite()");
  }
  return true;
}

bool CFileWriterBase::FlushBehindBuffer(absl::string_view src,
                                        FlushType flush_type) {
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::FlushBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!WriteMode())) return false;
  return BufferedWriter::FlushBehindBuffer(src, flush_type);
}

bool CFileWriterBase::SeekBehindBuffer(Position new_pos) {
  RIEGELI_ASSERT_NE(new_pos, pos())
      << "Failed precondition of BufferedWriter::SeekBehindBuffer(): "
         "position unchanged, use Seek() instead";
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::SeekBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!supports_random_access())) {
    // Delegate to base class version which fails, to avoid duplicating the
    // failure message here.
    return BufferedWriter::SeekBehindBuffer(new_pos);
  }
  if (ABSL_PREDICT_FALSE(!ok())) return false;
  read_mode_ = false;
  FILE* const dest = DestFile();
  if (new_pos > start_pos()) {
    // Seeking forwards.
    if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(dest, 0, SEEK_END) != 0)) {
      return FailOperation(cfile_internal::kFSeekFunctionName);
    }
    const cfile_internal::Offset file_size = cfile_internal::FTell(dest);
    if (ABSL_PREDICT_FALSE(file_size < 0)) {
      return FailOperation(cfile_internal::kFTellFunctionName);
    }
    if (ABSL_PREDICT_FALSE(new_pos > IntCast<Position>(file_size))) {
      // Stream ends.
      set_start_pos(IntCast<Position>(file_size));
      return false;
    }
  }
  if (ABSL_PREDICT_FALSE(
          cfile_internal::FSeek(dest, IntCast<cfile_internal::Offset>(new_pos),
                                SEEK_SET) != 0)) {
    return FailOperation(cfile_internal::kFSeekFunctionName);
  }
  set_start_pos(new_pos);
  return true;
}

absl::optional<Position> CFileWriterBase::SizeBehindBuffer() {
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::SizeBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!supports_random_access())) {
    // Delegate to base class version which fails, to avoid duplicating the
    // failure message here.
    return BufferedWriter::SizeBehindBuffer();
  }
  if (ABSL_PREDICT_FALSE(!ok())) return absl::nullopt;
  read_mode_ = false;
  FILE* const dest = DestFile();
  if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(dest, 0, SEEK_END) != 0)) {
    FailOperation(cfile_internal::kFSeekFunctionName);
    return absl::nullopt;
  }
  const cfile_internal::Offset file_size = cfile_internal::FTell(dest);
  if (ABSL_PREDICT_FALSE(file_size < 0)) {
    FailOperation(cfile_internal::kFTellFunctionName);
    return absl::nullopt;
  }
  if (ABSL_PREDICT_FALSE(cfile_internal::FSeek(
                             dest, IntCast<cfile_internal::Offset>(start_pos()),
                             SEEK_SET) != 0)) {
    FailOperation(cfile_internal::kFSeekFunctionName);
    return absl::nullopt;
  }
  return IntCast<Position>(file_size);
}

Reader* CFileWriterBase::ReadModeBehindBuffer(Position initial_pos) {
  RIEGELI_ASSERT_EQ(start_to_limit(), 0u)
      << "Failed precondition of BufferedWriter::ReadModeBehindBuffer(): "
         "buffer not empty";
  if (ABSL_PREDICT_FALSE(!supports_read_mode())) {
    // Delegate to base class version which fails, to avoid duplicating the
    // failure message here.
    return BufferedWriter::ReadModeBehindBuffer(initial_pos);
  }
  if (ABSL_PREDICT_FALSE(!ok())) return nullptr;
  FILE* const dest = DestFile();
  CFileReader<UnownedCFile>* const reader = associated_reader_.ResetReader(
      dest, CFileReaderBase::Options()
                .set_assumed_filename(filename())
                .set_buffer_options(buffer_options()));
  read_mode_ = true;
  reader->Seek(initial_pos);
  return reader;
}

}  // namespace riegeli

{'fraction_non_alphanumeric': 0.12061361457334611, 'fraction_numerical': 0.004065196548418025, 'mean_word_length': 3.3958192852326365, 'pattern_counts': {'":': 0, '<': 115, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 19, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6861670', 'n_tokens_mistral': 8460, 'n_tokens_neox': 8335, 'n_words': 1762}