| // Copyright 2020 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "gn/string_output_buffer.h" | |
| #include "base/files/file_path.h" | |
| #include "base/files/file_util.h" | |
| #include "gn/err.h" | |
| #include "gn/file_writer.h" | |
| #include "gn/filesystem_utils.h" | |
| #include <fstream> | |
| std::string StringOutputBuffer::str() const { | |
| std::string result; | |
| size_t data_size = size(); | |
| result.reserve(data_size); | |
| for (size_t nn = 0; nn < pages_.size(); ++nn) { | |
| size_t wanted_size = std::min(kPageSize, data_size - nn * kPageSize); | |
| result.append(pages_[nn]->data(), wanted_size); | |
| } | |
| return result; | |
| } | |
| void StringOutputBuffer::Append(const char* str, size_t len) { | |
| Append(std::string_view(str, len)); | |
| } | |
| void StringOutputBuffer::Append(std::string_view str) { | |
| while (str.size() > 0) { | |
| if (page_free_size() == 0) { | |
| // Allocate a new page. | |
| pages_.push_back(std::make_unique<Page>()); | |
| pos_ = 0; | |
| } | |
| size_t size = std::min(page_free_size(), str.size()); | |
| memcpy(pages_.back()->data() + pos_, str.data(), size); | |
| pos_ += size; | |
| str.remove_prefix(size); | |
| } | |
| } | |
| void StringOutputBuffer::Append(char c) { | |
| if (page_free_size() == 0) { | |
| // Allocate a new page. | |
| pages_.push_back(std::make_unique<Page>()); | |
| pos_ = 0; | |
| } | |
| pages_.back()->data()[pos_] = c; | |
| pos_ += 1; | |
| } | |
| bool StringOutputBuffer::ContentsEqual(const base::FilePath& file_path) const { | |
| // Compare file and stream sizes first. Quick and will save us some time if | |
| // they are different sizes. | |
| size_t data_size = size(); | |
| int64_t file_size; | |
| if (!base::GetFileSize(file_path, &file_size) || | |
| static_cast<size_t>(file_size) != data_size) { | |
| return false; | |
| } | |
| // Open the file in binary mode. | |
| std::ifstream file(file_path.As8Bit().c_str(), std::ios::binary); | |
| if (!file.is_open()) | |
| return false; | |
| size_t page_count = pages_.size(); | |
| Page file_page; | |
| for (size_t nn = 0; nn < page_count; ++nn) { | |
| size_t wanted_size = std::min(data_size - nn * kPageSize, kPageSize); | |
| file.read(file_page.data(), wanted_size); | |
| if (!file.good()) | |
| return false; | |
| if (memcmp(file_page.data(), pages_[nn]->data(), wanted_size) != 0) | |
| return false; | |
| } | |
| return true; | |
| } | |
| // Write the contents of this instance to a file at |file_path|. | |
| bool StringOutputBuffer::WriteToFile(const base::FilePath& file_path, | |
| Err* err) const { | |
| // Create the directory if necessary. | |
| if (!base::CreateDirectory(file_path.DirName())) { | |
| if (err) { | |
| *err = | |
| Err(Location(), "Unable to create directory.", | |
| "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\"."); | |
| } | |
| return false; | |
| } | |
| size_t data_size = size(); | |
| size_t page_count = pages_.size(); | |
| FileWriter writer; | |
| bool success = writer.Create(file_path); | |
| if (success) { | |
| for (size_t nn = 0; nn < page_count; ++nn) { | |
| size_t wanted_size = std::min(data_size - nn * kPageSize, kPageSize); | |
| success = writer.Write(std::string_view(pages_[nn]->data(), wanted_size)); | |
| if (!success) | |
| break; | |
| } | |
| } | |
| if (!writer.Close()) | |
| success = false; | |
| if (!success && err) { | |
| *err = Err(Location(), "Unable to write file.", | |
| "I was writing \"" + FilePathToUTF8(file_path) + "\"."); | |
| } | |
| return success; | |
| } | |
| bool StringOutputBuffer::WriteToFileIfChanged(const base::FilePath& file_path, | |
| Err* err) const { | |
| if (ContentsEqual(file_path)) | |
| return true; | |
| return WriteToFile(file_path, err); | |
| } |