blob: 1f6b98b352e1ffd3eb1268a13d4193d4a67edd93 [file] [log] [blame]
// Copyright 2017 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 "content/browser/download/parallel_download_utils.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "content/public/browser/download_save_info.h"
#include "content/public/common/content_features.h"
namespace content {
namespace {
// Default value for |kMinSliceSizeFinchKey|, when no parameter is specified.
const int64_t kMinSliceSizeParallelDownload = 1365333;
// Default value for |kParallelRequestCountFinchKey|, when no parameter is
// specified.
const int kParallelRequestCount = 3;
// The default remaining download time in seconds required for parallel request
// creation.
const int kDefaultRemainingTimeInSeconds = 2;
// TODO(qinmin): replace this with a comparator operator in
// DownloadItem::ReceivedSlice.
bool compareReceivedSlices(const DownloadItem::ReceivedSlice& lhs,
const DownloadItem::ReceivedSlice& rhs) {
return lhs.offset < rhs.offset;
}
} // namespace
std::vector<DownloadItem::ReceivedSlice> FindSlicesForRemainingContent(
int64_t current_offset,
int64_t total_length,
int request_count,
int64_t min_slice_size) {
std::vector<DownloadItem::ReceivedSlice> new_slices;
if (request_count > 0) {
int64_t slice_size =
std::max<int64_t>(total_length / request_count, min_slice_size);
slice_size = slice_size > 0 ? slice_size : 1;
for (int i = 0, num_requests = total_length / slice_size;
i < num_requests - 1; ++i) {
new_slices.emplace_back(current_offset, slice_size);
current_offset += slice_size;
}
}
// Last slice is a half open slice, which results in sending range request
// like "Range:50-" to fetch from 50 bytes to the end of the file.
new_slices.emplace_back(current_offset, DownloadSaveInfo::kLengthFullContent);
return new_slices;
}
std::vector<DownloadItem::ReceivedSlice> FindSlicesToDownload(
const std::vector<DownloadItem::ReceivedSlice>& received_slices) {
std::vector<DownloadItem::ReceivedSlice> result;
if (received_slices.empty()) {
result.emplace_back(0, DownloadSaveInfo::kLengthFullContent);
return result;
}
std::vector<DownloadItem::ReceivedSlice>::const_iterator iter =
received_slices.begin();
DCHECK_GE(iter->offset, 0);
if (iter->offset != 0)
result.emplace_back(0, iter->offset);
while (true) {
int64_t offset = iter->offset + iter->received_bytes;
std::vector<DownloadItem::ReceivedSlice>::const_iterator next =
std::next(iter);
if (next == received_slices.end()) {
result.emplace_back(offset, DownloadSaveInfo::kLengthFullContent);
break;
}
DCHECK_GE(next->offset, offset);
if (next->offset > offset)
result.emplace_back(offset, next->offset - offset);
iter = next;
}
return result;
}
size_t AddOrMergeReceivedSliceIntoSortedArray(
const DownloadItem::ReceivedSlice& new_slice,
std::vector<DownloadItem::ReceivedSlice>& received_slices) {
std::vector<DownloadItem::ReceivedSlice>::iterator it =
std::upper_bound(received_slices.begin(), received_slices.end(),
new_slice, compareReceivedSlices);
if (it != received_slices.begin()) {
std::vector<DownloadItem::ReceivedSlice>::iterator prev = std::prev(it);
if (prev->offset + prev->received_bytes == new_slice.offset) {
prev->received_bytes += new_slice.received_bytes;
return static_cast<size_t>(std::distance(received_slices.begin(), prev));
}
}
it = received_slices.emplace(it, new_slice);
return static_cast<size_t>(std::distance(received_slices.begin(), it));
}
int64_t GetMinSliceSizeConfig() {
std::string finch_value = base::GetFieldTrialParamValueByFeature(
features::kParallelDownloading, kMinSliceSizeFinchKey);
int64_t result;
return base::StringToInt64(finch_value, &result)
? result
: kMinSliceSizeParallelDownload;
}
int GetParallelRequestCountConfig() {
std::string finch_value = base::GetFieldTrialParamValueByFeature(
features::kParallelDownloading, kParallelRequestCountFinchKey);
int result;
return base::StringToInt(finch_value, &result) ? result
: kParallelRequestCount;
}
base::TimeDelta GetParallelRequestDelayConfig() {
std::string finch_value = base::GetFieldTrialParamValueByFeature(
features::kParallelDownloading, kParallelRequestDelayFinchKey);
int64_t time_ms = 0;
return base::StringToInt64(finch_value, &time_ms)
? base::TimeDelta::FromMilliseconds(time_ms)
: base::TimeDelta::FromMilliseconds(0);
}
base::TimeDelta GetParallelRequestRemainingTimeConfig() {
std::string finch_value = base::GetFieldTrialParamValueByFeature(
features::kParallelDownloading, kParallelRequestRemainingTimeFinchKey);
int time_in_seconds = 0;
return base::StringToInt(finch_value, &time_in_seconds)
? base::TimeDelta::FromSeconds(time_in_seconds)
: base::TimeDelta::FromSeconds(kDefaultRemainingTimeInSeconds);
}
void DebugSlicesInfo(const DownloadItem::ReceivedSlices& slices) {
DVLOG(1) << "Received slices size : " << slices.size();
for (const auto& it : slices) {
DVLOG(1) << "Slice offset = " << it.offset
<< " , received_bytes = " << it.received_bytes;
}
}
int64_t GetMaxContiguousDataBlockSizeFromBeginning(
const DownloadItem::ReceivedSlices& slices) {
std::vector<DownloadItem::ReceivedSlice>::const_iterator iter =
slices.begin();
int64_t size = 0;
while (iter != slices.end() && iter->offset == size) {
size += iter->received_bytes;
iter++;
}
return size;
}
bool IsParallelDownloadEnabled() {
bool feature_enabled =
base::FeatureList::IsEnabled(features::kParallelDownloading);
// Disabled when |kEnableParallelDownloadFinchKey| Finch config is set to
// false.
bool enabled_parameter = GetFieldTrialParamByFeatureAsBool(
features::kParallelDownloading, kEnableParallelDownloadFinchKey, true);
return feature_enabled && enabled_parameter;
}
} // namespace content