Alex's Anthology of Algorithms Common Code for Contests in Concise C++
Miscellany / Fast IO

8.2 Fast IO

8-Miscellany/8.2_Fast_IO.cpp

View on GitHub

Fast input and output wrappers based on fread() and fwrite(). For most problems, ios::sync_with_stdio(false); cin.tie(nullptr); is simpler and fast enough. Use these classes when input is huge or when iostream overhead is measurable.

  • FastInput in(file) reads tokens from a FILE*, defaulting to stdin.
  • in >> x reads a non-whitespace token into char, std::string, integral types, or floating point types.
  • FastOutput out(file) writes to a FILE*, defaulting to stdout.
  • out << x writes char, C strings, std::string, integral types, or floating point types.
  • out.flush() writes any buffered output immediately.
  • set_in(name), set_out(name), and set_io(iname, oname) redirect standard input/output to files. For example, set_io("task.in", "task.out") is convenient for USACO-style problems.

The parser assumes valid input. Floating point input/output is provided for convenience, not as the main performance path.

Implementation

#include <cassert>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <string>
#include <type_traits>

void set_in(const std::string &name) {
  assert(freopen(name.c_str(), "r", stdin) != nullptr);
}

void set_out(const std::string &name) {
  assert(freopen(name.c_str(), "w", stdout) != nullptr);
}

void set_io(const std::string &iname, const std::string &oname) {
  set_in(iname);
  set_out(oname);
}

struct FastInput {
  static constexpr int BUF_SIZE = 1 << 20;
  FILE *file;
  char buf[BUF_SIZE];
  int pos = 0, len = 0;

  explicit FastInput(FILE *file_ = stdin) : file(file_) {}

  char get_char() {
    if (pos == len) {
      len = fread(buf, 1, BUF_SIZE, file);
      pos = 0;
      if (len == 0) {
        return 0;
      }
    }
    return buf[pos++];
  }

  char peek_char() {
    char c = get_char();
    if (c) {
      --pos;
    }
    return c;
  }

  void skip_blanks() {
    while (true) {
      char c = get_char();
      if (!c) {
        return;
      }
      if (!std::isspace(static_cast<unsigned char>(c))) {
        --pos;
        return;
      }
    }
  }

  FastInput &operator>>(char &c) {
    skip_blanks();
    c = get_char();
    return *this;
  }

  FastInput &operator>>(std::string &s) {
    skip_blanks();
    s.clear();
    while (true) {
      char c = get_char();
      if (!c || std::isspace(static_cast<unsigned char>(c))) {
        break;
      }
      s += c;
    }
    return *this;
  }

  FastInput &operator>>(bool &x) {
    int val;
    *this >> val;
    x = val != 0;
    return *this;
  }

  template<class T>
  typename std::enable_if<
      std::is_integral<T>::value && !std::is_same<T, bool>::value, FastInput &>::type
  operator>>(T &x) {
    skip_blanks();
    char c = get_char();
    bool neg = false;
    if (c == '-') {
      neg = true;
      c = get_char();
    }
    using U = typename std::make_unsigned<T>::type;
    U val = 0;
    while (c && !std::isspace(static_cast<unsigned char>(c))) {
      val = U(10) * val + U(c - '0');
      c = get_char();
    }
    if constexpr (std::is_signed<T>::value) {
      x = neg ? T(U(0) - val) : T(val);
    } else {
      x = T(val);
    }
    return *this;
  }

  template<class T>
  typename std::enable_if<std::is_floating_point<T>::value, FastInput &>::type operator>>(T &x) {
    std::string s;
    *this >> s;
    x = static_cast<T>(std::strtod(s.c_str(), nullptr));
    return *this;
  }
};

struct FastOutput {
  static constexpr int BUF_SIZE = 1 << 20;
  FILE *file;
  char buf[BUF_SIZE];
  int pos = 0;

  explicit FastOutput(FILE *file_ = stdout) : file(file_) {}
  ~FastOutput() { flush(); }

  void flush() {
    if (pos) {
      fwrite(buf, 1, pos, file);
      pos = 0;
    }
  }

  void put_char(char c) {
    if (pos == BUF_SIZE) {
      flush();
    }
    buf[pos++] = c;
  }

  FastOutput &operator<<(char c) {
    put_char(c);
    return *this;
  }

  FastOutput &operator<<(const char *s) {
    while (*s) {
      put_char(*s++);
    }
    return *this;
  }

  FastOutput &operator<<(const std::string &s) {
    for (char c : s) {
      put_char(c);
    }
    return *this;
  }

  FastOutput &operator<<(bool x) {
    put_char(x ? '1' : '0');
    return *this;
  }

  template<class T>
  typename std::enable_if<
      std::is_integral<T>::value && !std::is_same<T, bool>::value, FastOutput &>::type
  operator<<(T x) {
    if (x == 0) {
      put_char('0');
      return *this;
    }
    using U = typename std::make_unsigned<T>::type;
    U val;
    if constexpr (std::is_signed<T>::value) {
      if (x < 0) {
        put_char('-');
        val = U(0) - U(x);
      } else {
        val = U(x);
      }
    } else {
      val = x;
    }
    char s[32];
    int n = 0;
    while (val > 0) {
      s[n++] = char('0' + val % 10);
      val /= 10;
    }
    while (n--) {
      put_char(s[n]);
    }
    return *this;
  }

  template<class T>
  typename std::enable_if<std::is_floating_point<T>::value, FastOutput &>::type operator<<(T x) {
    char s[64];
    std::snprintf(s, sizeof(s), "%.17g", static_cast<double>(x));
    return *this << s;
  }
};

Example Usage

int main() {
  // set_io("file.in", "file.out");

  FILE *input = tmpfile();
  std::fputs("42 hello 3.5 -2147483648\n", input);
  std::rewind(input);

  FastInput in(input);
  int x;
  std::string s;
  double y;
  int z;
  in >> x >> s >> y >> z;
  assert(x == 42 && s == "hello" && y == 3.5);
  assert(z == std::numeric_limits<int>::min());
  std::fclose(input);

  FILE *output = tmpfile();
  {
    FastOutput out(output);
    out << x << ' ' << s << ' ' << y << ' ' << z << '\n';
    out.flush();
  }
  std::rewind(output);
  char buf[64] = {};
  std::fgets(buf, sizeof(buf), output);
  assert(std::strncmp(buf, "42 hello 3.5 -2147483648", 24) == 0);
  std::fclose(output);
  return 0;
}