1 // Copyright 2020 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 "gn/string_output_buffer.h"
6
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "gn/err.h"
10 #include "gn/file_writer.h"
11 #include "gn/filesystem_utils.h"
12
13 #include <fstream>
14
str() const15 std::string StringOutputBuffer::str() const {
16 std::string result;
17 size_t data_size = size();
18 result.reserve(data_size);
19 for (size_t nn = 0; nn < pages_.size(); ++nn) {
20 size_t wanted_size = std::min(kPageSize, data_size - nn * kPageSize);
21 result.append(pages_[nn]->data(), wanted_size);
22 }
23 return result;
24 }
25
Append(const char * str,size_t len)26 void StringOutputBuffer::Append(const char* str, size_t len) {
27 Append(std::string_view(str, len));
28 }
29
Append(std::string_view str)30 void StringOutputBuffer::Append(std::string_view str) {
31 while (str.size() > 0) {
32 if (page_free_size() == 0) {
33 // Allocate a new page.
34 pages_.push_back(std::make_unique<Page>());
35 pos_ = 0;
36 }
37 size_t size = std::min(page_free_size(), str.size());
38 memcpy(pages_.back()->data() + pos_, str.data(), size);
39 pos_ += size;
40 str.remove_prefix(size);
41 }
42 }
43
Append(char c)44 void StringOutputBuffer::Append(char c) {
45 if (page_free_size() == 0) {
46 // Allocate a new page.
47 pages_.push_back(std::make_unique<Page>());
48 pos_ = 0;
49 }
50 pages_.back()->data()[pos_] = c;
51 pos_ += 1;
52 }
53
ContentsEqual(const base::FilePath & file_path) const54 bool StringOutputBuffer::ContentsEqual(const base::FilePath& file_path) const {
55 // Compare file and stream sizes first. Quick and will save us some time if
56 // they are different sizes.
57 size_t data_size = size();
58 int64_t file_size;
59 if (!base::GetFileSize(file_path, &file_size) ||
60 static_cast<size_t>(file_size) != data_size) {
61 return false;
62 }
63
64 // Open the file in binary mode.
65 std::ifstream file(file_path.As8Bit().c_str(), std::ios::binary);
66 if (!file.is_open())
67 return false;
68
69 size_t page_count = pages_.size();
70 Page file_page;
71 for (size_t nn = 0; nn < page_count; ++nn) {
72 size_t wanted_size = std::min(data_size - nn * kPageSize, kPageSize);
73 file.read(file_page.data(), wanted_size);
74 if (!file.good())
75 return false;
76
77 if (memcmp(file_page.data(), pages_[nn]->data(), wanted_size) != 0)
78 return false;
79 }
80 return true;
81 }
82
83 // Write the contents of this instance to a file at |file_path|.
WriteToFile(const base::FilePath & file_path,Err * err) const84 bool StringOutputBuffer::WriteToFile(const base::FilePath& file_path,
85 Err* err) const {
86 // Create the directory if necessary.
87 if (!base::CreateDirectory(file_path.DirName())) {
88 if (err) {
89 *err =
90 Err(Location(), "Unable to create directory.",
91 "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\".");
92 }
93 return false;
94 }
95
96 size_t data_size = size();
97 size_t page_count = pages_.size();
98
99 FileWriter writer;
100 bool success = writer.Create(file_path);
101 if (success) {
102 for (size_t nn = 0; nn < page_count; ++nn) {
103 size_t wanted_size = std::min(data_size - nn * kPageSize, kPageSize);
104 success = writer.Write(std::string_view(pages_[nn]->data(), wanted_size));
105 if (!success)
106 break;
107 }
108 }
109 if (!writer.Close())
110 success = false;
111
112 if (!success && err) {
113 *err = Err(Location(), "Unable to write file.",
114 "I was writing \"" + FilePathToUTF8(file_path) + "\".");
115 }
116 return success;
117 }
118
WriteToFileIfChanged(const base::FilePath & file_path,Err * err) const119 bool StringOutputBuffer::WriteToFileIfChanged(const base::FilePath& file_path,
120 Err* err) const {
121 if (ContentsEqual(file_path))
122 return true;
123
124 return WriteToFile(file_path, err);
125 }
126