• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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