Files
thehub/libs/httpengine/range.cpp
2022-09-10 00:35:59 +02:00

281 lines
6.6 KiB
C++

/* This file is part of Flowee
*
* Copyright (c) 2017 Aleksei Ermakov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* For the full copy of the License see <http://www.gnu.org/licenses/>
*/
#include <QRegularExpression>
#include "range.h"
#include "range_p.h"
using namespace HttpEngine;
RangePrivate::RangePrivate(Range *range)
: q(range)
{
}
Range::Range()
: d(new RangePrivate(this))
{
d->from = 1;
d->to = 0;
d->dataSize = -1;
}
Range::Range(const QString &range, qint64 dataSize)
: d(new RangePrivate(this))
{
QRegularExpression regExp("^(\\d*)-(\\d*)$");
int from = 0, to = -1;
auto match = regExp.match(range.trimmed());
if (match.hasMatch()) {
QString fromStr = match.captured(1);
QString toStr = match.captured(2);
// If both strings are empty - range is invalid. Setting to out of
// bounds range and returning.
if (fromStr.isEmpty() && toStr.isEmpty()) {
d->from = 1;
d->to = 0;
d->dataSize = -1;
return;
}
bool okFrom = true, okTo = true;
if (!fromStr.isEmpty()) {
from = fromStr.toInt(&okFrom);
}
if (!toStr.isEmpty()) {
to = toStr.toInt(&okTo);
}
// If failed to parse value - set to invalid range and return.
if (!okFrom) {
d->from = 1;
d->to = 0;
d->dataSize = -1;
return;
}
if (!okTo) {
d->from = 1;
d->to = 0;
d->dataSize = -1;
return;
}
// In case of 'last N bytes' range (Ex.: "Range: bytes=-500"),
// set from to -to and to to -1
if (fromStr.isEmpty()) {
from = -to;
to = -1;
}
} else { // If regexp didn't match - set to invalid range and return.
d->from = 1;
d->to = 0;
d->dataSize = -1;
return;
}
d->from = from;
d->to = to;
d->dataSize = dataSize;
}
Range::Range(qint64 from, qint64 to, qint64 dataSize)
: d(new RangePrivate(this))
{
d->from = from;
d->to = to < 0 ? -1 : to;
d->dataSize = dataSize < 0 ? -1 : dataSize;
}
Range::Range(const Range &other, qint64 dataSize)
: d(new RangePrivate(this))
{
d->from = other.d->from;
d->to = other.d->to;
d->dataSize = dataSize;
}
Range::~Range()
{
delete d;
}
Range& Range::operator=(const Range &other)
{
if (&other != this) {
d->from = other.d->from;
d->to = other.d->to;
d->dataSize = other.d->dataSize;
}
return *this;
}
qint64 Range::from() const
{
// Last N bytes requested
if (d->from < 0 && d->dataSize != -1) {
// Check if data is smaller then requested range
if (- d->from >= d->dataSize) {
return 0;
}
return d->dataSize + d->from;
}
// Check if d->from is bigger than d->to or d->dataSize
if ((d->from > d->to && d->to != -1) ||
(d->from >= d->dataSize && d->dataSize != -1)) {
return 0;
}
return d->from;
}
qint64 Range::to() const
{
// Last N bytes requested
if (d->from < 0 && d->dataSize != -1) {
return d->dataSize - 1;
}
// Skip first N bytes requested
if (d->from > 0 && d->to == -1 && d->dataSize != -1) {
return d->dataSize - 1;
}
// Check if d->from is bigger then d->to
if (d->from > d->to && d->to != -1) {
return d->from;
}
// When d->to overshoots dataSize
if ((d->to >= d->dataSize || d->to == -1) && d->dataSize != -1) {
return d->dataSize - 1;
}
return d->to;
}
qint64 Range::length() const
{
if (!isValid()) {
return -1;
}
// Last n bytes
if (d->from < 0) {
return -(d->from);
}
// From and to are set
if (d->to >= 0) {
return d->to - d->from + 1;
}
// From to to end
if (d->dataSize >= 0) {
return d->dataSize - d->from;
}
return -1;
}
qint64 Range::dataSize() const
{
return d->dataSize;
}
bool Range::isValid() const
{
// Valid cases:
// 1. "-500/1000" => from: -500, to: -1; dataSize: 1000
// 2. "10-/1000" => from: 10, to: -1; dataSize: 1000
// 3. "10-600/1000" => from: 10, to: 600; dataSize: 1000
// 4. "-500/*" => from: -500, to: -1; dataSize: -1
// 5. "10-/*" => from: 10, to: -1; dataSize: -1
// 6. "10-600/*" => from: 10, to: 600; dataSize: -1
// DataSize is set
if (d->dataSize >= 0) {
if (d->from < 0) { // Last n bytes
// Check if from is in range of dataSize
if (d->dataSize + d->from >= 0) {
return true;
}
} else {
if (d->to <= -1) { // To isn't set, range is up to the end
// Check if from is in range of dataSize
if (d->from < d->dataSize) {
return true;
}
} else { // from, to and dataSize are set
if (d->from <= d->to && d->to < d->dataSize) {
return true;
}
}
}
} else { // dataSize is not set
if (d->from < 0) { // Last n bytes
return true;
} else {
if (d->to <= -1) { // To isn't set, range is up to the end
return true;
} else { // from and to are set
if (d->from <= d->to) {
return true;
}
}
}
}
return false;
}
QString Range::contentRange() const
{
QString fromStr, toStr, sizeStr = "*";
if (d->dataSize >= 0) {
if (isValid()) {
fromStr = QString::number(from());
toStr = QString::number(to());
sizeStr = QString::number(dataSize());
} else {
sizeStr = QString::number(dataSize());
}
} else {
if (isValid()) {
fromStr = QString::number(from());
toStr = QString::number(to());
} else {
return "";
}
}
if (fromStr.isEmpty() || toStr.isEmpty()) {
return QString("*/%1").arg(sizeStr);
}
return QString("%1-%2/%3").arg(fromStr, toStr, sizeStr);
}