1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "crazy_linker_line_reader.h"
6
7#include "crazy_linker_debug.h"
8
9// Set to 1 to enable debug logs here.
10#define DEBUG_LINE_READER 0
11
12#define LLOG(...) LOG_IF(DEBUG_LINE_READER, __VA_ARGS__)
13
14namespace crazy {
15
16LineReader::LineReader() : fd_(), buff_(buff0_) {
17  Reset();
18  eof_ = true;
19}
20
21LineReader::LineReader(const char* path) : fd_(), buff_(buff0_) { Open(path); }
22
23LineReader::~LineReader() { Reset(); }
24
25void LineReader::Open(const char* path) {
26  Reset();
27  eof_ = !fd_.OpenReadOnly(path);
28}
29
30void LineReader::Reset() {
31  if (buff_ != buff0_)
32    ::free(buff_);
33
34  eof_ = false;
35  line_start_ = 0;
36  line_len_ = 0;
37  buff_size_ = 0;
38  buff_capacity_ = sizeof buff0_;
39  buff_ = buff0_;
40}
41
42bool LineReader::GetNextLine() {
43  // Eat previous line.
44  line_start_ += line_len_;
45  line_len_ = 0;
46
47  for (;;) {
48    LLOG("%s: LOOP line_start=%d buff_size=%d buff_capacity=%d\n",
49         __FUNCTION__,
50         line_start_,
51         buff_size_,
52         buff_capacity_);
53
54    // Find the end of the current line in the current buffer. The result
55    // of memchr(_,_,0) is undefined, treated as not-found.
56    const char* line = buff_ + line_start_;
57    const size_t range = buff_size_ - line_start_;
58    const char* line_end;
59    if (range > 0)
60      line_end = reinterpret_cast<const char*>(::memchr(line, '\n', range));
61    else
62      line_end = NULL;
63    if (line_end != NULL) {
64      // Found one, return it directly.
65      line_len_ = static_cast<size_t>(line_end + 1 - line);
66      LLOG("%s: LINE line_start=%d line_len=%d '%.*s'\n",
67           __FUNCTION__,
68           line_start_,
69           line_len_,
70           line_len_,
71           buff_ + line_start_);
72      return true;
73    }
74
75    // Eat the start of the buffer
76    if (line_start_ > 0) {
77      ::memmove(buff_, buff_ + line_start_, buff_size_ - line_start_);
78      buff_size_ -= line_start_;
79      line_start_ = 0;
80      LLOG("%s: MOVE buff_size=%d\n", __FUNCTION__, buff_size_);
81    }
82
83    // Handle end of input now.
84    if (eof_) {
85      // If there is a last line that isn't terminated by a newline, and
86      // there is room for it in the buffer. Manually add a \n and return
87      // the line.
88      if (buff_size_ > 0 && buff_size_ < buff_capacity_) {
89        buff_[buff_size_++] = '\n';
90        line_len_ = buff_size_;
91        LLOG("%s: EOF_LINE buff_size=%d '%.*s'\n",
92             __FUNCTION__,
93             buff_size_,
94             buff_size_,
95             buff_);
96        return true;
97      }
98      // Otherwise, ignore the last line.
99      LLOG("%s: EOF\n", __FUNCTION__);
100      return false;
101    }
102
103    // Before reading more data, grow the buffer if needed.
104    if (buff_size_ == buff_capacity_) {
105      size_t new_capacity = buff_capacity_ * 2;
106      void* old_buff = (buff_ == buff0_) ? NULL : buff_;
107      buff_ = static_cast<char*>(::realloc(old_buff, new_capacity));
108      if (old_buff != buff_)
109        ::memcpy(buff_, buff0_, buff_capacity_);
110
111      buff_capacity_ = new_capacity;
112      LLOG("%s: GROW buff_size=%d buff_capacity=%d '%.*s'\n",
113           __FUNCTION__,
114           buff_size_,
115           buff_capacity_,
116           buff_size_,
117           buff_);
118    }
119
120    // Try to fill the rest of buffer after current content.
121    size_t avail = buff_capacity_ - buff_size_;
122    int ret = fd_.Read(buff_ + buff_size_, avail);
123    LLOG("%s: READ buff_size=%d buff_capacity=%d avail=%d ret=%d\n",
124         __FUNCTION__,
125         buff_size_,
126         buff_capacity_,
127         avail,
128         ret);
129    if (ret <= 0) {
130      eof_ = true;
131      ret = 0;
132    }
133    buff_size_ += static_cast<size_t>(ret);
134  }
135}
136
137const char* LineReader::line() const { return buff_ + line_start_; }
138
139size_t LineReader::length() const { return line_len_; }
140
141}  // namespace crazy
142