#include "net/disk_cache/sql/sql_entry_impl.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/sql/sql_backend_impl.h"
namespace disk_cache {
SqlEntryImpl::SqlEntryImpl(base::WeakPtr<SqlBackendImpl> backend,
CacheEntryKey key,
scoped_refptr<ResIdOrErrorHolder> res_id_or_error,
base::Time last_used,
int64_t body_end,
scoped_refptr<net::GrowableIOBuffer> head)
: backend_(backend),
key_(key),
res_id_or_error_(std::move(res_id_or_error)),
last_used_(last_used),
body_end_(body_end),
head_(head ? std::move(head)
: base::MakeRefCounted<net::GrowableIOBuffer>()) {}
SqlEntryImpl::~SqlEntryImpl() {
if (!backend_) {
return;
}
if (doomed_) {
backend_->ReleaseDoomedEntry(*this);
} else {
if (previous_header_size_in_storage_.has_value() ||
new_hints_.has_value()) {
const int64_t header_size_delta =
static_cast<int64_t>(head_->size()) -
previous_header_size_in_storage_.value_or(0);
backend_->UpdateEntryHeaderAndLastUsed(key_, res_id_or_error_, last_used_,
new_hints_, head_,
header_size_delta);
} else if (last_used_modified_) {
backend_->UpdateEntryLastUsed(key_, res_id_or_error_, last_used_);
}
backend_->ReleaseActiveEntry(*this);
}
}
void SqlEntryImpl::Doom() {
if (doomed_ || !backend_) {
return;
}
backend_->DoomActiveEntry(*this);
}
void SqlEntryImpl::Close() {
Release();
}
std::string SqlEntryImpl::GetKey() const {
return key_.string();
}
base::Time SqlEntryImpl::GetLastUsed() const {
return last_used_;
}
int64_t SqlEntryImpl::GetDataSize(int index) const {
if (index != 0 && index != 1) {
return net::ERR_INVALID_ARGUMENT;
}
if (index == 0) {
return head_->size();
}
CHECK_EQ(index, 1);
return body_end_;
}
int SqlEntryImpl::ReadData(int index,
int64_t offset,
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
UpdateLastUsed();
if (index != 0 && index != 1) {
return net::ERR_INVALID_ARGUMENT;
}
if (buf_len == 0) {
return 0;
}
if (!buf || buf_len < 0 || offset < 0 ||
offset > std::numeric_limits<int>::max()) {
return net::ERR_INVALID_ARGUMENT;
}
if (index == 1) {
return ReadDataInternal(offset, buf, buf_len, std::move(callback),
false);
}
CHECK_EQ(index, 0);
if (head_->size() <= offset) {
return 0;
}
buf_len = std::min(buf_len, head_->size() - static_cast<int>(offset));
buf->first(buf_len).copy_from_nonoverlapping(head_->span().subspan(
static_cast<size_t>(offset), static_cast<size_t>(buf_len)));
return buf_len;
}
int SqlEntryImpl::ReadDataInternal(int64_t offset,
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
bool sparse_reading) {
if (!backend_) {
return net::ERR_FAILED;
}
if (body_end_ <= offset) {
return 0;
}
return backend_->ReadEntryData(key_, res_id_or_error_, offset, buf, buf_len,
body_end_, sparse_reading,
std::move(callback));
}
int SqlEntryImpl::WriteData(int index,
int64_t offset,
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
bool truncate) {
UpdateLastUsed();
if ((index != 0 && index != 1) || (offset < 0) || (buf_len < 0) ||
(!buf && buf_len > 0) || !base::CheckAdd(offset, buf_len).IsValid()) {
return net::ERR_INVALID_ARGUMENT;
}
if (offset + buf_len > std::numeric_limits<int>::max()) {
return net::ERR_INVALID_ARGUMENT;
}
if (index == 1) {
return WriteDataInternal(offset, buf, buf_len, std::move(callback),
truncate, false);
}
CHECK_EQ(index, 0);
if (!previous_header_size_in_storage_) {
previous_header_size_in_storage_ = head_->size();
}
size_t u_offset = base::checked_cast<size_t>(offset);
size_t u_buf_len = base::checked_cast<size_t>(buf_len);
if (offset == 0 && truncate) {
head_->SetCapacity(buf_len);
if (buf_len) {
head_->span().copy_from(buf->first(u_buf_len));
}
} else {
const size_t original_size = head_->size();
const size_t buffer_size =
truncate ? u_offset + u_buf_len
: std::max(u_offset + u_buf_len, original_size);
head_->SetCapacity(base::checked_cast<int>(buffer_size));
const size_t fill_size =
u_offset <= original_size ? 0 : u_offset - original_size;
if (fill_size > 0) {
std::ranges::fill(head_->span().subspan(original_size, fill_size), 0);
}
if (buf) {
head_->span().subspan(u_offset).copy_prefix_from(buf->first(u_buf_len));
}
}
return buf_len;
}
int SqlEntryImpl::WriteDataInternal(int64_t offset,
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback,
bool truncate,
bool sparse_write) {
if (!backend_) {
return net::ERR_FAILED;
}
if (buf_len == 0) {
if (truncate ? (offset == body_end_) : (offset <= body_end_)) {
return buf_len;
}
}
const int64_t end_offset = base::CheckAdd(offset, buf_len).ValueOrDie();
const int64_t new_body_end =
truncate ? end_offset : std::max(end_offset, body_end_);
if (!sparse_write && new_body_end > backend_->MaxFileSize()) {
return net::ERR_FAILED;
}
const auto old_body_end = body_end_;
body_end_ = new_body_end;
return backend_->WriteEntryData(key_, res_id_or_error_, old_body_end,
body_end_, offset, buf, buf_len, truncate,
std::move(callback));
}
int SqlEntryImpl::ReadSparseData(int64_t offset,
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
UpdateLastUsed();
if (buf_len == 0) {
return net::OK;
}
if (!buf || buf_len < 0 || offset < 0) {
return net::ERR_INVALID_ARGUMENT;
}
return ReadDataInternal(offset, buf, buf_len, std::move(callback),
true);
}
int SqlEntryImpl::WriteSparseData(int64_t offset,
IOBuffer* buf,
int buf_len,
CompletionOnceCallback callback) {
UpdateLastUsed();
if ((offset < 0) || (buf_len < 0) || (!buf && buf_len > 0) ||
!base::CheckAdd(offset, buf_len).IsValid()) {
return net::ERR_INVALID_ARGUMENT;
}
return WriteDataInternal(offset, buf, buf_len, std::move(callback),
false, true);
}
RangeResult SqlEntryImpl::GetAvailableRange(int64_t offset,
int len,
RangeResultCallback callback) {
if (!backend_) {
return RangeResult(net::ERR_FAILED);
}
if (offset < 0 || len < 0) {
return RangeResult(net::ERR_INVALID_ARGUMENT);
}
return backend_->GetEntryAvailableRange(key_, res_id_or_error_, offset, len,
std::move(callback));
}
bool SqlEntryImpl::CouldBeSparse() const {
return true;
}
void SqlEntryImpl::CancelSparseIO() {
}
net::Error SqlEntryImpl::ReadyForSparseIO(CompletionOnceCallback callback) {
return net::OK;
}
void SqlEntryImpl::SetEntryInMemoryData(uint8_t data) {
const MemoryEntryDataHints hints(data);
new_hints_ = hints;
if (backend_) {
backend_->SetEntryDataHints(key_, res_id_or_error_, hints);
}
}
void SqlEntryImpl::SetLastUsedTimeForTest(base::Time time) {
last_used_ = time;
last_used_modified_ = true;
}
void SqlEntryImpl::UpdateLastUsed() {
last_used_ = base::Time::Now();
last_used_modified_ = true;
}
void SqlEntryImpl::MarkAsDoomed() {
doomed_ = true;
}
}