TCPSPSuite
filetools.hpp
1 #ifndef TCPSPSUITE_FILETOOLS_HPP
2 #define TCPSPSUITE_FILETOOLS_HPP
3 
4 #include <algorithm>
5 #include <cassert>
6 #include <fstream>
7 #include <iomanip>
8 #include <iostream>
9 #include <iterator>
10 #include <sstream>
11 #include <string>
12 #include <vector>
13 
14 namespace util {
15 
16 class LineStorage {
17 public:
18  LineStorage(std::ifstream & f_in) : f(f_in) { this->read_lines(); }
19 
20  size_t
21  line_at_byte(size_t byte) const noexcept
22  {
23  auto line_it = std::upper_bound(this->byte_prefix_sum.begin(),
24  this->byte_prefix_sum.end(), byte);
25  // can never be .begin() - the first entry is 0
26 
27  auto line_count = std::distance(this->byte_prefix_sum.begin(), line_it) - 1;
28  assert(line_count >= 0);
29  return static_cast<size_t>(line_count);
30  }
31 
32  const std::string &
33  get_line(size_t index) const
34  {
35  return this->lines.at(index);
36  }
37 
38  size_t
39  get_line_byte_prefix_sum(size_t index) const
40  {
41  return this->byte_prefix_sum.at(index);
42  }
43 
44  size_t
45  line_count() const noexcept
46  {
47  return this->lines.size();
48  }
49 
50 private:
51  void
52  read_lines()
53  {
54  auto old_streampos = this->f.tellg();
55  this->f.seekg(0);
56 
57  for (std::string s; std::getline(this->f, s);) {
58  if (not s.empty()) {
59  this->byte_prefix_sum.push_back(static_cast<size_t>(this->f.tellg()));
60  this->lines.push_back(s);
61  }
62  }
63 
64  this->f.seekg(old_streampos);
65  }
66  std::ifstream & f;
67  std::vector<std::string> lines;
68  std::vector<size_t> byte_prefix_sum;
69 };
70 
71 class FileContextGiver {
72 public:
73  FileContextGiver(std::string filename_in, size_t byte_in,
74  size_t context_lines = 3)
75  : byte(byte_in), context(context_lines), filename(std::move(filename_in)),
76  f(filename), ls(f)
77  {
78  this->build_context();
79  }
80 
81  const std::vector<std::string> &
82  get_message() const noexcept
83  {
84  return this->message;
85  }
86 
87 private:
88  void
89  build_context()
90  {
91  size_t relevant_line = this->ls.line_at_byte(this->byte);
92  for (size_t l = static_cast<size_t>(
93  std::max(0l, static_cast<long>(relevant_line) -
94  static_cast<long>(this->context)));
95  l < relevant_line; ++l) {
96  this->add_line(l);
97  }
98  this->add_line(relevant_line);
99  this->add_indicator(relevant_line, byte);
100 
101  for (size_t l = relevant_line + 1;
102  l < std::min(this->ls.line_count(), relevant_line + this->context + 1);
103  ++l) {
104  this->add_line(l);
105  }
106  }
107 
108  void
109  add_line(size_t index)
110  {
111  std::ostringstream line;
112  line << std::setfill(' ') << std::setw(4) << index << " | ";
113  std::string raw_line = this->ls.get_line(index);
114  std::replace(raw_line.begin(), raw_line.end(), '\t', ' ');
115  line << raw_line;
116  this->message.push_back(line.str());
117  }
118 
119  void
120  add_indicator(size_t line_index, size_t at_byte)
121  {
122  size_t indent = at_byte - this->ls.get_line_byte_prefix_sum(line_index);
123  std::ostringstream indicator;
124  std::string prefix(indent + 7, ' ');
125  indicator << prefix;
126  indicator << "^";
127  this->message.push_back(indicator.str());
128  }
129 
130  size_t byte;
131  size_t context;
132  std::string filename;
133  std::ifstream f;
134 
135  LineStorage ls;
136  std::vector<std::string> message;
137 };
138 
139 } // namespace util
140 
141 #endif