-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathpar2calc.cpp
129 lines (110 loc) · 3.72 KB
/
par2calc.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "par2calc.h"
#include "settings.h"
static quint64 simpleCountFromSize(quint64 size, const SrcFileList& files)
{
quint64 count = 0;
for(const auto& file : files) {
count += (file.size() + size-1) / size;
}
return count;
}
quint64 Par2Calc::sliceSizeFromCount(int& count, quint64 multiple, int limit, const SrcFileList& files, int fileCount, int moveDir)
{
if(files.isEmpty()) { // should never happen
count = 0;
return 0;
}
if(fileCount > limit) { // impossible to satisfy
return 0;
}
if(count > limit)
count = limit;
// from par2gen.js/calcSliceSizeForFiles:
// there may be a better algorithm to do this, but we'll use a binary search approach to find the correct number of slices to use
quint64 lbound = multiple;
quint64 ubound = 0;
for(const auto& file : files)
if(file.size() > ubound)
ubound = file.size();
if(ubound == 0) // all files are empty
return 0;
if(count < fileCount) {
count = fileCount;
return ubound;
}
auto mod = ubound % multiple;
if(mod)
ubound += multiple - mod;
while(lbound < ubound - multiple) {
quint64 mid = ((ubound + lbound) / (multiple*2)) * multiple;
quint64 testCount = simpleCountFromSize(mid, files);
if(count >= testCount)
ubound = mid;
else
lbound = mid;
}
quint64 lboundSlices = simpleCountFromSize(lbound, files);
quint64 uboundSlices = simpleCountFromSize(ubound, files);
if(lboundSlices == count)
return lbound;
if(uboundSlices == count)
return ubound;
if(lboundSlices > limit) {
// higher slice count is invalid, must use lower count
count = uboundSlices;
return ubound;
}
// prefer closer target (as long as we're not specifically moving in a direction, i.e. SpinBox)
if(moveDir == 0) {
int lboundDiff = abs(static_cast<int>(lboundSlices)-count);
int uboundDiff = abs(static_cast<int>(uboundSlices)-count);
if(lboundDiff < uboundDiff) {
count = lboundSlices;
return lbound;
} else if(uboundDiff < lboundDiff) {
count = uboundSlices;
return ubound;
}
}
if(moveDir > 0) {
count = lboundSlices;
return lbound;
} else {
// we generally prefer the lower slice count, since we know we can't exceed limits that way
count = uboundSlices;
return ubound;
}
}
int Par2Calc::sliceCountFromSize(quint64& size, quint64 multiple, int limit, const SrcFileList& files, int fileCount)
{
int mod = size % multiple;
if(mod)
size += multiple - mod;
if(size == 0)
size = 4;
quint64 count = simpleCountFromSize(size, files);
if(count > limit) {
int limitedCount = limit;
size = sliceSizeFromCount(limitedCount, multiple, limit, files, fileCount);
count = limitedCount;
}
return count;
}
int Par2Calc::maxSliceCount(quint64 multiple, int limit, const SrcFileList& files)
{
quint64 count = 0;
for(const auto& file : files) {
count += (file.size() + multiple-1) / multiple;
if(count > limit) return limit;
}
return count;
}
int Par2Calc::round_down_pow2(int v) {
if((v & (v-1)) == 0) return v; // is a power of 2 (shortcut exists because this is the common case)
// find target number via a float conversion
// (usage of float over double does mean that this can be wrong for very large numbers, but we're not expecting these)
union { float f; uint32_t u; } tmp;
tmp.f = (float)v; // convert to float
tmp.u &= 0xff800000; // discard mantissa
return (int)tmp.f; // convert back to int
}