blob: c5d91ff5ddaec0f38c9b92009366be2fe8592c24 [file] [log] [blame]
// 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);
}