1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/files/FileStats.h"
8
9 #include "td/telegram/files/FileLoaderUtils.h"
10 #include "td/telegram/td_api.h"
11
12 #include "td/utils/algorithm.h"
13 #include "td/utils/common.h"
14 #include "td/utils/format.h"
15 #include "td/utils/misc.h"
16
17 #include <algorithm>
18 #include <unordered_set>
19 #include <utility>
20
21 namespace td {
22
get_storage_statistics_fast_object() const23 tl_object_ptr<td_api::storageStatisticsFast> FileStatsFast::get_storage_statistics_fast_object() const {
24 return make_tl_object<td_api::storageStatisticsFast>(size, count, database_size, language_pack_database_size,
25 log_size);
26 }
27
add(StatByType & by_type,FileType file_type,int64 size)28 void FileStats::add(StatByType &by_type, FileType file_type, int64 size) {
29 auto pos = static_cast<size_t>(file_type);
30 CHECK(pos < stat_by_type_.size());
31 by_type[pos].size += size;
32 by_type[pos].cnt++;
33 }
34
add_impl(const FullFileInfo & info)35 void FileStats::add_impl(const FullFileInfo &info) {
36 if (split_by_owner_dialog_id_) {
37 add(stat_by_owner_dialog_id_[info.owner_dialog_id], info.file_type, info.size);
38 } else {
39 add(stat_by_type_, info.file_type, info.size);
40 }
41 }
42
add_copy(const FullFileInfo & info)43 void FileStats::add_copy(const FullFileInfo &info) {
44 add_impl(info);
45 if (need_all_files_) {
46 all_files_.push_back(info);
47 }
48 }
49
add(FullFileInfo && info)50 void FileStats::add(FullFileInfo &&info) {
51 add_impl(info);
52 if (need_all_files_) {
53 all_files_.push_back(std::move(info));
54 }
55 }
56
get_nontemp_stat(const FileStats::StatByType & by_type)57 FileTypeStat FileStats::get_nontemp_stat(const FileStats::StatByType &by_type) {
58 FileTypeStat stat;
59 for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
60 if (FileType(i) != FileType::Temp) {
61 stat.size += by_type[i].size;
62 stat.cnt += by_type[i].cnt;
63 }
64 }
65 return stat;
66 }
67
get_total_nontemp_stat() const68 FileTypeStat FileStats::get_total_nontemp_stat() const {
69 if (!split_by_owner_dialog_id_) {
70 return get_nontemp_stat(stat_by_type_);
71 }
72 FileTypeStat stat;
73 for (auto &dialog : stat_by_owner_dialog_id_) {
74 auto tmp = get_nontemp_stat(dialog.second);
75 stat.size += tmp.size;
76 stat.cnt += tmp.cnt;
77 }
78 return stat;
79 }
80
apply_dialog_limit(int32 limit)81 void FileStats::apply_dialog_limit(int32 limit) {
82 if (limit == -1) {
83 return;
84 }
85 if (!split_by_owner_dialog_id_) {
86 return;
87 }
88
89 std::vector<std::pair<int64, DialogId>> dialogs;
90 for (auto &dialog : stat_by_owner_dialog_id_) {
91 if (!dialog.first.is_valid()) {
92 continue;
93 }
94 int64 size = 0;
95 for (auto &it : dialog.second) {
96 size += it.size;
97 }
98 dialogs.emplace_back(size, dialog.first);
99 }
100 size_t prefix = dialogs.size();
101 if (prefix > static_cast<size_t>(limit)) {
102 prefix = static_cast<size_t>(limit);
103 }
104 std::partial_sort(dialogs.begin(), dialogs.begin() + prefix, dialogs.end(),
105 [](const auto &x, const auto &y) { return x.first > y.first; });
106 dialogs.resize(prefix);
107
108 apply_dialog_ids(transform(dialogs, [](const auto &dialog) { return dialog.second; }));
109 }
110
apply_dialog_ids(const vector<DialogId> & dialog_ids)111 void FileStats::apply_dialog_ids(const vector<DialogId> &dialog_ids) {
112 std::unordered_set<DialogId, DialogIdHash> all_dialogs(dialog_ids.begin(), dialog_ids.end());
113 StatByType other_stats;
114 bool other_flag = false;
115 for (auto it = stat_by_owner_dialog_id_.begin(); it != stat_by_owner_dialog_id_.end();) {
116 if (all_dialogs.count(it->first)) {
117 ++it;
118 } else {
119 for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
120 other_stats[i].size += it->second[i].size;
121 other_stats[i].cnt += it->second[i].cnt;
122 }
123 other_flag = true;
124 it = stat_by_owner_dialog_id_.erase(it);
125 }
126 }
127
128 if (other_flag) {
129 DialogId other_dialog_id; // prevents MSVC warning C4709: comma operator within array index expression
130 stat_by_owner_dialog_id_[other_dialog_id] = other_stats;
131 }
132 }
133
get_storage_statistics_by_chat_object(DialogId dialog_id,const FileStats::StatByType & stat_by_type_)134 td_api::object_ptr<td_api::storageStatisticsByChat> FileStats::get_storage_statistics_by_chat_object(
135 DialogId dialog_id, const FileStats::StatByType &stat_by_type_) {
136 auto stats = make_tl_object<td_api::storageStatisticsByChat>(dialog_id.get(), 0, 0, Auto());
137 FileStats::StatByType aggregated_stats;
138 for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
139 auto file_type = narrow_cast<size_t>(get_main_file_type(static_cast<FileType>(i)));
140 aggregated_stats[file_type].size += stat_by_type_[i].size;
141 aggregated_stats[file_type].cnt += stat_by_type_[i].cnt;
142 }
143
144 for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
145 auto size = aggregated_stats[i].size;
146 auto cnt = aggregated_stats[i].cnt;
147
148 if (size == 0) {
149 continue;
150 }
151
152 auto file_type = static_cast<FileType>(i);
153 stats->size_ += size;
154 stats->count_ += cnt;
155 stats->by_file_type_.push_back(
156 make_tl_object<td_api::storageStatisticsByFileType>(get_file_type_object(file_type), size, cnt));
157 }
158 return stats;
159 }
160
get_storage_statistics_object() const161 tl_object_ptr<td_api::storageStatistics> FileStats::get_storage_statistics_object() const {
162 auto stats = make_tl_object<td_api::storageStatistics>(0, 0, Auto());
163 if (!split_by_owner_dialog_id_) {
164 stats->by_chat_.reserve(1);
165 stats->by_chat_.push_back(get_storage_statistics_by_chat_object(DialogId(), stat_by_type_));
166 } else {
167 stats->by_chat_.reserve(stat_by_owner_dialog_id_.size());
168 for (auto &by_dialog : stat_by_owner_dialog_id_) {
169 stats->by_chat_.push_back(get_storage_statistics_by_chat_object(by_dialog.first, by_dialog.second));
170 }
171 std::sort(stats->by_chat_.begin(), stats->by_chat_.end(), [](const auto &x, const auto &y) {
172 if (x->chat_id_ == 0 || y->chat_id_ == 0) {
173 return (x->chat_id_ == 0) < (y->chat_id_ == 0);
174 }
175 return x->size_ > y->size_;
176 });
177 }
178 for (const auto &by_dialog : stats->by_chat_) {
179 stats->size_ += by_dialog->size_;
180 stats->count_ += by_dialog->count_;
181 }
182 return stats;
183 }
184
get_dialog_ids() const185 vector<DialogId> FileStats::get_dialog_ids() const {
186 vector<DialogId> res;
187 if (!split_by_owner_dialog_id_) {
188 return res;
189 }
190 res.reserve(stat_by_owner_dialog_id_.size());
191 for (auto &by_dialog : stat_by_owner_dialog_id_) {
192 if (by_dialog.first.is_valid()) {
193 res.push_back(by_dialog.first);
194 }
195 }
196 return res;
197 }
198
get_all_files()199 vector<FullFileInfo> FileStats::get_all_files() {
200 return std::move(all_files_);
201 }
202
operator <<(StringBuilder & sb,const FileTypeStat & stat)203 static StringBuilder &operator<<(StringBuilder &sb, const FileTypeStat &stat) {
204 return sb << tag("size", format::as_size(stat.size)) << tag("count", stat.cnt);
205 }
206
operator <<(StringBuilder & sb,const FileStats & file_stats)207 StringBuilder &operator<<(StringBuilder &sb, const FileStats &file_stats) {
208 if (!file_stats.split_by_owner_dialog_id_) {
209 FileTypeStat total_stat;
210 for (auto &type_stat : file_stats.stat_by_type_) {
211 total_stat.size += type_stat.size;
212 total_stat.cnt += type_stat.cnt;
213 }
214
215 sb << "[FileStat " << tag("total", total_stat);
216 for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
217 sb << tag(get_file_type_name(FileType(i)), file_stats.stat_by_type_[i]);
218 }
219 sb << "]";
220 } else {
221 {
222 FileTypeStat total_stat;
223 for (auto &by_type : file_stats.stat_by_owner_dialog_id_) {
224 for (auto &type_stat : by_type.second) {
225 total_stat.size += type_stat.size;
226 total_stat.cnt += type_stat.cnt;
227 }
228 }
229 sb << "[FileStat " << tag("total", total_stat);
230 }
231 for (auto &by_type : file_stats.stat_by_owner_dialog_id_) {
232 FileTypeStat dialog_stat;
233 for (auto &type_stat : by_type.second) {
234 dialog_stat.size += type_stat.size;
235 dialog_stat.cnt += type_stat.cnt;
236 }
237
238 sb << "[FileStat " << tag("owner_dialog_id", by_type.first) << tag("total", dialog_stat);
239 for (int32 i = 0; i < MAX_FILE_TYPE; i++) {
240 sb << tag(get_file_type_name(FileType(i)), by_type.second[i]);
241 }
242 sb << "]";
243 }
244 sb << "]";
245 }
246
247 return sb;
248 }
249
250 } // namespace td
251