1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2018 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker *
4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker *
8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker *
10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker */
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker #include "dexanalyze_strings.h"
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Worker #include <algorithm>
20*795d594fSAndroid Build Coastguard Worker #include <iomanip>
21*795d594fSAndroid Build Coastguard Worker #include <iostream>
22*795d594fSAndroid Build Coastguard Worker #include <queue>
23*795d594fSAndroid Build Coastguard Worker
24*795d594fSAndroid Build Coastguard Worker #include "base/time_utils.h"
25*795d594fSAndroid Build Coastguard Worker #include "dex/class_accessor-inl.h"
26*795d594fSAndroid Build Coastguard Worker #include "dex/code_item_accessors-inl.h"
27*795d594fSAndroid Build Coastguard Worker #include "dex/dex_instruction-inl.h"
28*795d594fSAndroid Build Coastguard Worker
29*795d594fSAndroid Build Coastguard Worker namespace art {
30*795d594fSAndroid Build Coastguard Worker namespace dexanalyze {
31*795d594fSAndroid Build Coastguard Worker
32*795d594fSAndroid Build Coastguard Worker // Tunable parameters.
33*795d594fSAndroid Build Coastguard Worker static const size_t kMinPrefixLen = 1;
34*795d594fSAndroid Build Coastguard Worker static const size_t kMaxPrefixLen = 255;
35*795d594fSAndroid Build Coastguard Worker static const size_t kPrefixConstantCost = 4;
36*795d594fSAndroid Build Coastguard Worker static const size_t kPrefixIndexCost = 2;
37*795d594fSAndroid Build Coastguard Worker
38*795d594fSAndroid Build Coastguard Worker class PrefixDictionary {
39*795d594fSAndroid Build Coastguard Worker public:
40*795d594fSAndroid Build Coastguard Worker // Add prefix data and return the offset to the start of the added data.
AddPrefixData(const uint8_t * data,size_t len)41*795d594fSAndroid Build Coastguard Worker size_t AddPrefixData(const uint8_t* data, size_t len) {
42*795d594fSAndroid Build Coastguard Worker const size_t offset = prefix_data_.size();
43*795d594fSAndroid Build Coastguard Worker prefix_data_.insert(prefix_data_.end(), data, data + len);
44*795d594fSAndroid Build Coastguard Worker return offset;
45*795d594fSAndroid Build Coastguard Worker }
46*795d594fSAndroid Build Coastguard Worker
47*795d594fSAndroid Build Coastguard Worker static constexpr size_t kLengthBits = 8;
48*795d594fSAndroid Build Coastguard Worker static constexpr size_t kLengthMask = (1u << kLengthBits) - 1;
49*795d594fSAndroid Build Coastguard Worker
50*795d594fSAndroid Build Coastguard Worker // Return the prefix offset and length.
GetOffset(uint32_t prefix_index,uint32_t * offset,uint32_t * length) const51*795d594fSAndroid Build Coastguard Worker ALWAYS_INLINE void GetOffset(uint32_t prefix_index, uint32_t* offset, uint32_t* length) const {
52*795d594fSAndroid Build Coastguard Worker CHECK_LT(prefix_index, offsets_.size());
53*795d594fSAndroid Build Coastguard Worker const uint32_t data = offsets_[prefix_index];
54*795d594fSAndroid Build Coastguard Worker *length = data & kLengthMask;
55*795d594fSAndroid Build Coastguard Worker *offset = data >> kLengthBits;
56*795d594fSAndroid Build Coastguard Worker }
57*795d594fSAndroid Build Coastguard Worker
AddOffset(uint32_t offset,uint32_t length)58*795d594fSAndroid Build Coastguard Worker uint32_t AddOffset(uint32_t offset, uint32_t length) {
59*795d594fSAndroid Build Coastguard Worker CHECK_LE(length, kLengthMask);
60*795d594fSAndroid Build Coastguard Worker offsets_.push_back((offset << kLengthBits) | length);
61*795d594fSAndroid Build Coastguard Worker return offsets_.size() - 1;
62*795d594fSAndroid Build Coastguard Worker }
63*795d594fSAndroid Build Coastguard Worker
64*795d594fSAndroid Build Coastguard Worker public:
65*795d594fSAndroid Build Coastguard Worker std::vector<uint32_t> offsets_;
66*795d594fSAndroid Build Coastguard Worker std::vector<uint8_t> prefix_data_;
67*795d594fSAndroid Build Coastguard Worker };
68*795d594fSAndroid Build Coastguard Worker
69*795d594fSAndroid Build Coastguard Worker class PrefixStrings {
70*795d594fSAndroid Build Coastguard Worker public:
71*795d594fSAndroid Build Coastguard Worker class Builder {
72*795d594fSAndroid Build Coastguard Worker public:
Builder(PrefixStrings * output)73*795d594fSAndroid Build Coastguard Worker explicit Builder(PrefixStrings* output) : output_(output) {}
74*795d594fSAndroid Build Coastguard Worker void Build(const std::vector<std::string>& strings);
75*795d594fSAndroid Build Coastguard Worker
76*795d594fSAndroid Build Coastguard Worker private:
77*795d594fSAndroid Build Coastguard Worker PrefixStrings* const output_;
78*795d594fSAndroid Build Coastguard Worker };
79*795d594fSAndroid Build Coastguard Worker
80*795d594fSAndroid Build Coastguard Worker // Return the string index that was added.
AddString(uint16_t prefix,const std::string & str)81*795d594fSAndroid Build Coastguard Worker size_t AddString(uint16_t prefix, const std::string& str) {
82*795d594fSAndroid Build Coastguard Worker const size_t string_offset = chars_.size();
83*795d594fSAndroid Build Coastguard Worker chars_.push_back(static_cast<uint8_t>(prefix >> 8));
84*795d594fSAndroid Build Coastguard Worker chars_.push_back(static_cast<uint8_t>(prefix >> 0));
85*795d594fSAndroid Build Coastguard Worker EncodeUnsignedLeb128(&chars_, str.length());
86*795d594fSAndroid Build Coastguard Worker const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&str[0]);
87*795d594fSAndroid Build Coastguard Worker chars_.insert(chars_.end(), ptr, ptr + str.length());
88*795d594fSAndroid Build Coastguard Worker string_offsets_.push_back(string_offset);
89*795d594fSAndroid Build Coastguard Worker return string_offsets_.size() - 1;
90*795d594fSAndroid Build Coastguard Worker }
91*795d594fSAndroid Build Coastguard Worker
GetString(uint32_t string_idx) const92*795d594fSAndroid Build Coastguard Worker std::string GetString(uint32_t string_idx) const {
93*795d594fSAndroid Build Coastguard Worker const size_t offset = string_offsets_[string_idx];
94*795d594fSAndroid Build Coastguard Worker const uint8_t* suffix_data = &chars_[offset];
95*795d594fSAndroid Build Coastguard Worker uint16_t prefix_idx = (static_cast<uint16_t>(suffix_data[0]) << 8) +
96*795d594fSAndroid Build Coastguard Worker suffix_data[1];
97*795d594fSAndroid Build Coastguard Worker suffix_data += 2;
98*795d594fSAndroid Build Coastguard Worker uint32_t prefix_offset;
99*795d594fSAndroid Build Coastguard Worker uint32_t prefix_len;
100*795d594fSAndroid Build Coastguard Worker dictionary_.GetOffset(prefix_idx, &prefix_offset, &prefix_len);
101*795d594fSAndroid Build Coastguard Worker const uint8_t* prefix_data = &dictionary_.prefix_data_[prefix_offset];
102*795d594fSAndroid Build Coastguard Worker std::string ret(prefix_data, prefix_data + prefix_len);
103*795d594fSAndroid Build Coastguard Worker uint32_t suffix_len = DecodeUnsignedLeb128(&suffix_data);
104*795d594fSAndroid Build Coastguard Worker ret.insert(ret.end(), suffix_data, suffix_data + suffix_len);
105*795d594fSAndroid Build Coastguard Worker return ret;
106*795d594fSAndroid Build Coastguard Worker }
107*795d594fSAndroid Build Coastguard Worker
Equal(uint32_t string_idx,const uint8_t * data,size_t len) const108*795d594fSAndroid Build Coastguard Worker ALWAYS_INLINE bool Equal(uint32_t string_idx, const uint8_t* data, size_t len) const {
109*795d594fSAndroid Build Coastguard Worker const size_t offset = string_offsets_[string_idx];
110*795d594fSAndroid Build Coastguard Worker const uint8_t* suffix_data = &chars_[offset];
111*795d594fSAndroid Build Coastguard Worker uint16_t prefix_idx = (static_cast<uint16_t>(suffix_data[0]) << 8) +
112*795d594fSAndroid Build Coastguard Worker suffix_data[1];
113*795d594fSAndroid Build Coastguard Worker suffix_data += 2;
114*795d594fSAndroid Build Coastguard Worker uint32_t prefix_offset;
115*795d594fSAndroid Build Coastguard Worker uint32_t prefix_len;
116*795d594fSAndroid Build Coastguard Worker dictionary_.GetOffset(prefix_idx, &prefix_offset, &prefix_len);
117*795d594fSAndroid Build Coastguard Worker uint32_t suffix_len = DecodeUnsignedLeb128(&suffix_data);
118*795d594fSAndroid Build Coastguard Worker if (prefix_len + suffix_len != len) {
119*795d594fSAndroid Build Coastguard Worker return false;
120*795d594fSAndroid Build Coastguard Worker }
121*795d594fSAndroid Build Coastguard Worker const uint8_t* prefix_data = &dictionary_.prefix_data_[prefix_offset];
122*795d594fSAndroid Build Coastguard Worker if ((true)) {
123*795d594fSAndroid Build Coastguard Worker return memcmp(prefix_data, data, prefix_len) == 0u &&
124*795d594fSAndroid Build Coastguard Worker memcmp(suffix_data, data + prefix_len, len - prefix_len) == 0u;
125*795d594fSAndroid Build Coastguard Worker } else {
126*795d594fSAndroid Build Coastguard Worker len -= prefix_len;
127*795d594fSAndroid Build Coastguard Worker while (prefix_len != 0u) {
128*795d594fSAndroid Build Coastguard Worker if (*prefix_data++ != *data++) {
129*795d594fSAndroid Build Coastguard Worker return false;
130*795d594fSAndroid Build Coastguard Worker }
131*795d594fSAndroid Build Coastguard Worker --prefix_len;
132*795d594fSAndroid Build Coastguard Worker }
133*795d594fSAndroid Build Coastguard Worker while (len != 0u) {
134*795d594fSAndroid Build Coastguard Worker if (*suffix_data++ != *data++) {
135*795d594fSAndroid Build Coastguard Worker return false;
136*795d594fSAndroid Build Coastguard Worker }
137*795d594fSAndroid Build Coastguard Worker --len;
138*795d594fSAndroid Build Coastguard Worker }
139*795d594fSAndroid Build Coastguard Worker return true;
140*795d594fSAndroid Build Coastguard Worker }
141*795d594fSAndroid Build Coastguard Worker }
142*795d594fSAndroid Build Coastguard Worker
143*795d594fSAndroid Build Coastguard Worker public:
144*795d594fSAndroid Build Coastguard Worker PrefixDictionary dictionary_;
145*795d594fSAndroid Build Coastguard Worker std::vector<uint8_t> chars_;
146*795d594fSAndroid Build Coastguard Worker std::vector<uint32_t> string_offsets_;
147*795d594fSAndroid Build Coastguard Worker };
148*795d594fSAndroid Build Coastguard Worker
149*795d594fSAndroid Build Coastguard Worker // Normal non prefix strings.
150*795d594fSAndroid Build Coastguard Worker class NormalStrings {
151*795d594fSAndroid Build Coastguard Worker public:
152*795d594fSAndroid Build Coastguard Worker // Return the string index that was added.
AddString(const std::string & str)153*795d594fSAndroid Build Coastguard Worker size_t AddString(const std::string& str) {
154*795d594fSAndroid Build Coastguard Worker const size_t string_offset = chars_.size();
155*795d594fSAndroid Build Coastguard Worker EncodeUnsignedLeb128(&chars_, str.length());
156*795d594fSAndroid Build Coastguard Worker const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&str[0]);
157*795d594fSAndroid Build Coastguard Worker chars_.insert(chars_.end(), ptr, ptr + str.length());
158*795d594fSAndroid Build Coastguard Worker string_offsets_.push_back(string_offset);
159*795d594fSAndroid Build Coastguard Worker return string_offsets_.size() - 1;
160*795d594fSAndroid Build Coastguard Worker }
161*795d594fSAndroid Build Coastguard Worker
GetString(uint32_t string_idx) const162*795d594fSAndroid Build Coastguard Worker std::string GetString(uint32_t string_idx) const {
163*795d594fSAndroid Build Coastguard Worker const size_t offset = string_offsets_[string_idx];
164*795d594fSAndroid Build Coastguard Worker const uint8_t* data = &chars_[offset];
165*795d594fSAndroid Build Coastguard Worker uint32_t len = DecodeUnsignedLeb128(&data);
166*795d594fSAndroid Build Coastguard Worker return std::string(data, data + len);
167*795d594fSAndroid Build Coastguard Worker }
168*795d594fSAndroid Build Coastguard Worker
Equal(uint32_t string_idx,const uint8_t * data,size_t len) const169*795d594fSAndroid Build Coastguard Worker ALWAYS_INLINE bool Equal(uint32_t string_idx, const uint8_t* data, size_t len) const {
170*795d594fSAndroid Build Coastguard Worker const size_t offset = string_offsets_[string_idx];
171*795d594fSAndroid Build Coastguard Worker const uint8_t* str_data = &chars_[offset];
172*795d594fSAndroid Build Coastguard Worker uint32_t str_len = DecodeUnsignedLeb128(&str_data);
173*795d594fSAndroid Build Coastguard Worker if (str_len != len) {
174*795d594fSAndroid Build Coastguard Worker return false;
175*795d594fSAndroid Build Coastguard Worker }
176*795d594fSAndroid Build Coastguard Worker return memcmp(data, str_data, len) == 0u;
177*795d594fSAndroid Build Coastguard Worker }
178*795d594fSAndroid Build Coastguard Worker
179*795d594fSAndroid Build Coastguard Worker public:
180*795d594fSAndroid Build Coastguard Worker std::vector<uint8_t> chars_;
181*795d594fSAndroid Build Coastguard Worker std::vector<uint32_t> string_offsets_;
182*795d594fSAndroid Build Coastguard Worker };
183*795d594fSAndroid Build Coastguard Worker
184*795d594fSAndroid Build Coastguard Worker // Node value = (distance from root) * (occurrences - 1).
185*795d594fSAndroid Build Coastguard Worker class MatchTrie {
186*795d594fSAndroid Build Coastguard Worker public:
Add(const std::string & str)187*795d594fSAndroid Build Coastguard Worker MatchTrie* Add(const std::string& str) {
188*795d594fSAndroid Build Coastguard Worker MatchTrie* node = this;
189*795d594fSAndroid Build Coastguard Worker size_t depth = 0u;
190*795d594fSAndroid Build Coastguard Worker for (uint8_t c : str) {
191*795d594fSAndroid Build Coastguard Worker ++depth;
192*795d594fSAndroid Build Coastguard Worker if (node->nodes_[c] == nullptr) {
193*795d594fSAndroid Build Coastguard Worker MatchTrie* new_node = new MatchTrie();
194*795d594fSAndroid Build Coastguard Worker node->nodes_[c].reset(new_node);
195*795d594fSAndroid Build Coastguard Worker new_node->parent_ = node;
196*795d594fSAndroid Build Coastguard Worker new_node->depth_ = depth;
197*795d594fSAndroid Build Coastguard Worker new_node->incoming_ = c;
198*795d594fSAndroid Build Coastguard Worker node = new_node;
199*795d594fSAndroid Build Coastguard Worker } else {
200*795d594fSAndroid Build Coastguard Worker node = node->nodes_[c].get();
201*795d594fSAndroid Build Coastguard Worker }
202*795d594fSAndroid Build Coastguard Worker ++node->count_;
203*795d594fSAndroid Build Coastguard Worker }
204*795d594fSAndroid Build Coastguard Worker return node;
205*795d594fSAndroid Build Coastguard Worker }
206*795d594fSAndroid Build Coastguard Worker
207*795d594fSAndroid Build Coastguard Worker // Returns the length of the longest prefix and if it's a leaf node.
LongestPrefix(const std::string & str)208*795d594fSAndroid Build Coastguard Worker MatchTrie* LongestPrefix(const std::string& str) {
209*795d594fSAndroid Build Coastguard Worker MatchTrie* node = this;
210*795d594fSAndroid Build Coastguard Worker for (uint8_t c : str) {
211*795d594fSAndroid Build Coastguard Worker if (node->nodes_[c] == nullptr) {
212*795d594fSAndroid Build Coastguard Worker break;
213*795d594fSAndroid Build Coastguard Worker }
214*795d594fSAndroid Build Coastguard Worker node = node->nodes_[c].get();
215*795d594fSAndroid Build Coastguard Worker }
216*795d594fSAndroid Build Coastguard Worker return node;
217*795d594fSAndroid Build Coastguard Worker }
218*795d594fSAndroid Build Coastguard Worker
IsLeaf() const219*795d594fSAndroid Build Coastguard Worker bool IsLeaf() const {
220*795d594fSAndroid Build Coastguard Worker for (const std::unique_ptr<MatchTrie>& cur_node : nodes_) {
221*795d594fSAndroid Build Coastguard Worker if (cur_node != nullptr) {
222*795d594fSAndroid Build Coastguard Worker return false;
223*795d594fSAndroid Build Coastguard Worker }
224*795d594fSAndroid Build Coastguard Worker }
225*795d594fSAndroid Build Coastguard Worker return true;
226*795d594fSAndroid Build Coastguard Worker }
227*795d594fSAndroid Build Coastguard Worker
Savings() const228*795d594fSAndroid Build Coastguard Worker int32_t Savings() const {
229*795d594fSAndroid Build Coastguard Worker int32_t cost = kPrefixConstantCost;
230*795d594fSAndroid Build Coastguard Worker int32_t first_used = 0u;
231*795d594fSAndroid Build Coastguard Worker if (chosen_suffix_count_ == 0u) {
232*795d594fSAndroid Build Coastguard Worker cost += depth_;
233*795d594fSAndroid Build Coastguard Worker }
234*795d594fSAndroid Build Coastguard Worker uint32_t extra_savings = 0u;
235*795d594fSAndroid Build Coastguard Worker for (MatchTrie* cur = parent_; cur != nullptr; cur = cur->parent_) {
236*795d594fSAndroid Build Coastguard Worker if (cur->chosen_) {
237*795d594fSAndroid Build Coastguard Worker first_used = cur->depth_;
238*795d594fSAndroid Build Coastguard Worker if (cur->chosen_suffix_count_ == 0u) {
239*795d594fSAndroid Build Coastguard Worker // First suffix for the chosen parent, remove the cost of the dictionary entry.
240*795d594fSAndroid Build Coastguard Worker extra_savings += first_used;
241*795d594fSAndroid Build Coastguard Worker }
242*795d594fSAndroid Build Coastguard Worker break;
243*795d594fSAndroid Build Coastguard Worker }
244*795d594fSAndroid Build Coastguard Worker }
245*795d594fSAndroid Build Coastguard Worker return count_ * (depth_ - first_used) - cost + extra_savings;
246*795d594fSAndroid Build Coastguard Worker }
247*795d594fSAndroid Build Coastguard Worker
248*795d594fSAndroid Build Coastguard Worker template <typename T, typename... Args, template <typename...> class Queue>
PopRealTop(Queue<T,Args...> & queue)249*795d594fSAndroid Build Coastguard Worker T PopRealTop(Queue<T, Args...>& queue) {
250*795d594fSAndroid Build Coastguard Worker auto pair = queue.top();
251*795d594fSAndroid Build Coastguard Worker queue.pop();
252*795d594fSAndroid Build Coastguard Worker // Keep updating values until one sticks.
253*795d594fSAndroid Build Coastguard Worker while (pair.second->Savings() != pair.first) {
254*795d594fSAndroid Build Coastguard Worker pair.first = pair.second->Savings();
255*795d594fSAndroid Build Coastguard Worker queue.push(pair);
256*795d594fSAndroid Build Coastguard Worker pair = queue.top();
257*795d594fSAndroid Build Coastguard Worker queue.pop();
258*795d594fSAndroid Build Coastguard Worker }
259*795d594fSAndroid Build Coastguard Worker return pair;
260*795d594fSAndroid Build Coastguard Worker }
261*795d594fSAndroid Build Coastguard Worker
ExtractPrefixes(size_t max)262*795d594fSAndroid Build Coastguard Worker std::vector<std::string> ExtractPrefixes(size_t max) {
263*795d594fSAndroid Build Coastguard Worker std::vector<std::string> ret;
264*795d594fSAndroid Build Coastguard Worker // Make priority queue and adaptively update it. Each node value is the savings from picking
265*795d594fSAndroid Build Coastguard Worker // it. Insert all of the interesting nodes in the queue (children != 1).
266*795d594fSAndroid Build Coastguard Worker std::priority_queue<std::pair<int32_t, MatchTrie*>> queue;
267*795d594fSAndroid Build Coastguard Worker // Add all of the nodes to the queue.
268*795d594fSAndroid Build Coastguard Worker std::vector<MatchTrie*> work(1, this);
269*795d594fSAndroid Build Coastguard Worker while (!work.empty()) {
270*795d594fSAndroid Build Coastguard Worker MatchTrie* elem = work.back();
271*795d594fSAndroid Build Coastguard Worker work.pop_back();
272*795d594fSAndroid Build Coastguard Worker size_t num_childs = 0u;
273*795d594fSAndroid Build Coastguard Worker for (const std::unique_ptr<MatchTrie>& child : elem->nodes_) {
274*795d594fSAndroid Build Coastguard Worker if (child != nullptr) {
275*795d594fSAndroid Build Coastguard Worker work.push_back(child.get());
276*795d594fSAndroid Build Coastguard Worker ++num_childs;
277*795d594fSAndroid Build Coastguard Worker }
278*795d594fSAndroid Build Coastguard Worker }
279*795d594fSAndroid Build Coastguard Worker if (num_childs > 1u || elem->value_ != 0u) {
280*795d594fSAndroid Build Coastguard Worker queue.emplace(elem->Savings(), elem);
281*795d594fSAndroid Build Coastguard Worker }
282*795d594fSAndroid Build Coastguard Worker }
283*795d594fSAndroid Build Coastguard Worker std::priority_queue<std::pair<int32_t, MatchTrie*>> prefixes;
284*795d594fSAndroid Build Coastguard Worker // The savings can only ever go down for a given node, never up.
285*795d594fSAndroid Build Coastguard Worker while (max != 0u && !queue.empty()) {
286*795d594fSAndroid Build Coastguard Worker std::pair<int32_t, MatchTrie*> pair = PopRealTop(queue);
287*795d594fSAndroid Build Coastguard Worker if (pair.second != this && pair.first > 0) {
288*795d594fSAndroid Build Coastguard Worker // Pick this node.
289*795d594fSAndroid Build Coastguard Worker uint32_t count = pair.second->count_;
290*795d594fSAndroid Build Coastguard Worker pair.second->chosen_ = true;
291*795d594fSAndroid Build Coastguard Worker for (MatchTrie* cur = pair.second->parent_; cur != this; cur = cur->parent_) {
292*795d594fSAndroid Build Coastguard Worker if (cur->chosen_) {
293*795d594fSAndroid Build Coastguard Worker break;
294*795d594fSAndroid Build Coastguard Worker }
295*795d594fSAndroid Build Coastguard Worker cur->count_ -= count;
296*795d594fSAndroid Build Coastguard Worker }
297*795d594fSAndroid Build Coastguard Worker for (MatchTrie* cur = pair.second->parent_; cur != this; cur = cur->parent_) {
298*795d594fSAndroid Build Coastguard Worker ++cur->chosen_suffix_count_;
299*795d594fSAndroid Build Coastguard Worker }
300*795d594fSAndroid Build Coastguard Worker prefixes.emplace(pair.first, pair.second);
301*795d594fSAndroid Build Coastguard Worker --max;
302*795d594fSAndroid Build Coastguard Worker } else {
303*795d594fSAndroid Build Coastguard Worker // Negative or no EV, just delete the node.
304*795d594fSAndroid Build Coastguard Worker }
305*795d594fSAndroid Build Coastguard Worker }
306*795d594fSAndroid Build Coastguard Worker while (!prefixes.empty()) {
307*795d594fSAndroid Build Coastguard Worker std::pair<int32_t, MatchTrie*> pair = PopRealTop(prefixes);
308*795d594fSAndroid Build Coastguard Worker if (pair.first <= 0) {
309*795d594fSAndroid Build Coastguard Worker continue;
310*795d594fSAndroid Build Coastguard Worker }
311*795d594fSAndroid Build Coastguard Worker ret.push_back(pair.second->GetString());
312*795d594fSAndroid Build Coastguard Worker }
313*795d594fSAndroid Build Coastguard Worker return ret;
314*795d594fSAndroid Build Coastguard Worker }
315*795d594fSAndroid Build Coastguard Worker
GetString() const316*795d594fSAndroid Build Coastguard Worker std::string GetString() const {
317*795d594fSAndroid Build Coastguard Worker std::vector<uint8_t> chars;
318*795d594fSAndroid Build Coastguard Worker for (const MatchTrie* cur = this; cur->parent_ != nullptr; cur = cur->parent_) {
319*795d594fSAndroid Build Coastguard Worker chars.push_back(cur->incoming_);
320*795d594fSAndroid Build Coastguard Worker }
321*795d594fSAndroid Build Coastguard Worker return std::string(chars.rbegin(), chars.rend());
322*795d594fSAndroid Build Coastguard Worker }
323*795d594fSAndroid Build Coastguard Worker
324*795d594fSAndroid Build Coastguard Worker std::unique_ptr<MatchTrie> nodes_[256];
325*795d594fSAndroid Build Coastguard Worker MatchTrie* parent_ = nullptr;
326*795d594fSAndroid Build Coastguard Worker uint32_t count_ = 0u;
327*795d594fSAndroid Build Coastguard Worker uint32_t depth_ = 0u;
328*795d594fSAndroid Build Coastguard Worker int32_t savings_ = 0u;
329*795d594fSAndroid Build Coastguard Worker uint8_t incoming_ = 0u;
330*795d594fSAndroid Build Coastguard Worker // Value of the current node, non zero if the node is chosen.
331*795d594fSAndroid Build Coastguard Worker uint32_t value_ = 0u;
332*795d594fSAndroid Build Coastguard Worker // If the current node is chosen to be a used prefix.
333*795d594fSAndroid Build Coastguard Worker bool chosen_ = false;
334*795d594fSAndroid Build Coastguard Worker // If the current node is a prefix of a longer chosen prefix.
335*795d594fSAndroid Build Coastguard Worker uint32_t chosen_suffix_count_ = 0u;
336*795d594fSAndroid Build Coastguard Worker };
337*795d594fSAndroid Build Coastguard Worker
Build(const std::vector<std::string> & strings)338*795d594fSAndroid Build Coastguard Worker void PrefixStrings::Builder::Build(const std::vector<std::string>& strings) {
339*795d594fSAndroid Build Coastguard Worker std::unique_ptr<MatchTrie> prefixe_trie(new MatchTrie());
340*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < strings.size(); ++i) {
341*795d594fSAndroid Build Coastguard Worker size_t len = 0u;
342*795d594fSAndroid Build Coastguard Worker if (i > 0u) {
343*795d594fSAndroid Build Coastguard Worker CHECK_GT(strings[i], strings[i - 1]);
344*795d594fSAndroid Build Coastguard Worker len = std::max(len, PrefixLen(strings[i], strings[i - 1]));
345*795d594fSAndroid Build Coastguard Worker }
346*795d594fSAndroid Build Coastguard Worker if (i < strings.size() - 1) {
347*795d594fSAndroid Build Coastguard Worker len = std::max(len, PrefixLen(strings[i], strings[i + 1]));
348*795d594fSAndroid Build Coastguard Worker }
349*795d594fSAndroid Build Coastguard Worker len = std::min(len, kMaxPrefixLen);
350*795d594fSAndroid Build Coastguard Worker if (len >= kMinPrefixLen) {
351*795d594fSAndroid Build Coastguard Worker prefixe_trie->Add(strings[i].substr(0, len))->value_ = 1u;
352*795d594fSAndroid Build Coastguard Worker }
353*795d594fSAndroid Build Coastguard Worker }
354*795d594fSAndroid Build Coastguard Worker
355*795d594fSAndroid Build Coastguard Worker // Build prefixes.
356*795d594fSAndroid Build Coastguard Worker {
357*795d594fSAndroid Build Coastguard Worker static constexpr size_t kPrefixBits = 15;
358*795d594fSAndroid Build Coastguard Worker std::vector<std::string> prefixes(prefixe_trie->ExtractPrefixes(1 << kPrefixBits));
359*795d594fSAndroid Build Coastguard Worker // Add longest prefixes first so that subprefixes can share data.
360*795d594fSAndroid Build Coastguard Worker std::sort(prefixes.begin(), prefixes.end(), [](const std::string& a, const std::string& b) {
361*795d594fSAndroid Build Coastguard Worker return a.length() > b.length();
362*795d594fSAndroid Build Coastguard Worker });
363*795d594fSAndroid Build Coastguard Worker prefixe_trie.reset();
364*795d594fSAndroid Build Coastguard Worker prefixe_trie.reset(new MatchTrie());
365*795d594fSAndroid Build Coastguard Worker uint32_t prefix_idx = 0u;
366*795d594fSAndroid Build Coastguard Worker CHECK_EQ(output_->dictionary_.AddOffset(0u, 0u), prefix_idx++);
367*795d594fSAndroid Build Coastguard Worker for (const std::string& str : prefixes) {
368*795d594fSAndroid Build Coastguard Worker uint32_t prefix_offset = 0u;
369*795d594fSAndroid Build Coastguard Worker MatchTrie* node = prefixe_trie->LongestPrefix(str);
370*795d594fSAndroid Build Coastguard Worker if (node != nullptr && node->depth_ == str.length() && node->value_ != 0u) {
371*795d594fSAndroid Build Coastguard Worker CHECK_EQ(node->GetString(), str);
372*795d594fSAndroid Build Coastguard Worker uint32_t existing_len = 0u;
373*795d594fSAndroid Build Coastguard Worker output_->dictionary_.GetOffset(node->value_, &prefix_offset, &existing_len);
374*795d594fSAndroid Build Coastguard Worker // Make sure to register the current node.
375*795d594fSAndroid Build Coastguard Worker prefixe_trie->Add(str)->value_ = prefix_idx;
376*795d594fSAndroid Build Coastguard Worker } else {
377*795d594fSAndroid Build Coastguard Worker auto add_str = [&](const std::string& s) {
378*795d594fSAndroid Build Coastguard Worker node = prefixe_trie->Add(s);
379*795d594fSAndroid Build Coastguard Worker node->value_ = prefix_idx;
380*795d594fSAndroid Build Coastguard Worker while (node != nullptr) {
381*795d594fSAndroid Build Coastguard Worker node->value_ = prefix_idx;
382*795d594fSAndroid Build Coastguard Worker node = node->parent_;
383*795d594fSAndroid Build Coastguard Worker }
384*795d594fSAndroid Build Coastguard Worker };
385*795d594fSAndroid Build Coastguard Worker static constexpr size_t kNumSubstrings = 1u;
386*795d594fSAndroid Build Coastguard Worker // Increasing kNumSubstrings provides savings since it enables common substrings and not
387*795d594fSAndroid Build Coastguard Worker // only prefixes to share data. The problem is that it's slow.
388*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < std::min(str.length(), kNumSubstrings); ++i) {
389*795d594fSAndroid Build Coastguard Worker add_str(str.substr(i));
390*795d594fSAndroid Build Coastguard Worker }
391*795d594fSAndroid Build Coastguard Worker prefix_offset = output_->dictionary_.AddPrefixData(
392*795d594fSAndroid Build Coastguard Worker reinterpret_cast<const uint8_t*>(&str[0]),
393*795d594fSAndroid Build Coastguard Worker str.length());
394*795d594fSAndroid Build Coastguard Worker }
395*795d594fSAndroid Build Coastguard Worker // TODO: Validiate the prefix offset.
396*795d594fSAndroid Build Coastguard Worker CHECK_EQ(output_->dictionary_.AddOffset(prefix_offset, str.length()), prefix_idx);
397*795d594fSAndroid Build Coastguard Worker ++prefix_idx;
398*795d594fSAndroid Build Coastguard Worker }
399*795d594fSAndroid Build Coastguard Worker }
400*795d594fSAndroid Build Coastguard Worker
401*795d594fSAndroid Build Coastguard Worker // Add strings to the dictionary.
402*795d594fSAndroid Build Coastguard Worker for (const std::string& str : strings) {
403*795d594fSAndroid Build Coastguard Worker MatchTrie* node = prefixe_trie->LongestPrefix(str);
404*795d594fSAndroid Build Coastguard Worker uint32_t prefix_idx = 0u;
405*795d594fSAndroid Build Coastguard Worker uint32_t best_length = 0u;
406*795d594fSAndroid Build Coastguard Worker while (node != nullptr) {
407*795d594fSAndroid Build Coastguard Worker uint32_t offset = 0u;
408*795d594fSAndroid Build Coastguard Worker uint32_t length = 0u;
409*795d594fSAndroid Build Coastguard Worker output_->dictionary_.GetOffset(node->value_, &offset, &length);
410*795d594fSAndroid Build Coastguard Worker if (node->depth_ == length) {
411*795d594fSAndroid Build Coastguard Worker prefix_idx = node->value_;
412*795d594fSAndroid Build Coastguard Worker best_length = node->depth_;
413*795d594fSAndroid Build Coastguard Worker break;
414*795d594fSAndroid Build Coastguard Worker // Actually the prefix we want.
415*795d594fSAndroid Build Coastguard Worker }
416*795d594fSAndroid Build Coastguard Worker node = node->parent_;
417*795d594fSAndroid Build Coastguard Worker }
418*795d594fSAndroid Build Coastguard Worker output_->AddString(prefix_idx, str.substr(best_length));
419*795d594fSAndroid Build Coastguard Worker }
420*795d594fSAndroid Build Coastguard Worker }
421*795d594fSAndroid Build Coastguard Worker
ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>> & dex_files)422*795d594fSAndroid Build Coastguard Worker void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
423*795d594fSAndroid Build Coastguard Worker std::set<std::string> unique_strings;
424*795d594fSAndroid Build Coastguard Worker // Accumulate the strings.
425*795d594fSAndroid Build Coastguard Worker for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
426*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < dex_file->NumStringIds(); ++i) {
427*795d594fSAndroid Build Coastguard Worker uint32_t length = 0;
428*795d594fSAndroid Build Coastguard Worker const char* data = dex_file->GetStringDataAndUtf16Length(dex::StringIndex(i), &length);
429*795d594fSAndroid Build Coastguard Worker // Analyze if the string has any UTF16 chars.
430*795d594fSAndroid Build Coastguard Worker bool have_wide_char = false;
431*795d594fSAndroid Build Coastguard Worker const char* ptr = data;
432*795d594fSAndroid Build Coastguard Worker for (size_t j = 0; j < length; ++j) {
433*795d594fSAndroid Build Coastguard Worker have_wide_char = have_wide_char || GetUtf16FromUtf8(&ptr) >= 0x100;
434*795d594fSAndroid Build Coastguard Worker }
435*795d594fSAndroid Build Coastguard Worker if (have_wide_char) {
436*795d594fSAndroid Build Coastguard Worker wide_string_bytes_ += 2 * length;
437*795d594fSAndroid Build Coastguard Worker } else {
438*795d594fSAndroid Build Coastguard Worker ascii_string_bytes_ += length;
439*795d594fSAndroid Build Coastguard Worker }
440*795d594fSAndroid Build Coastguard Worker string_data_bytes_ += ptr - data;
441*795d594fSAndroid Build Coastguard Worker unique_strings.insert(data);
442*795d594fSAndroid Build Coastguard Worker }
443*795d594fSAndroid Build Coastguard Worker }
444*795d594fSAndroid Build Coastguard Worker // Unique strings only since we want to exclude savings from multi-dex duplication.
445*795d594fSAndroid Build Coastguard Worker ProcessStrings(std::vector<std::string>(unique_strings.begin(), unique_strings.end()));
446*795d594fSAndroid Build Coastguard Worker }
447*795d594fSAndroid Build Coastguard Worker
ProcessStrings(const std::vector<std::string> & strings)448*795d594fSAndroid Build Coastguard Worker void AnalyzeStrings::ProcessStrings(const std::vector<std::string>& strings) {
449*795d594fSAndroid Build Coastguard Worker // Calculate total shared prefix.
450*795d594fSAndroid Build Coastguard Worker size_t prefix_index_cost_ = 0u;
451*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < strings.size(); ++i) {
452*795d594fSAndroid Build Coastguard Worker size_t best_len = 0;
453*795d594fSAndroid Build Coastguard Worker if (i > 0) {
454*795d594fSAndroid Build Coastguard Worker best_len = std::max(best_len, PrefixLen(strings[i], strings[i - 1]));
455*795d594fSAndroid Build Coastguard Worker }
456*795d594fSAndroid Build Coastguard Worker if (i < strings.size() - 1) {
457*795d594fSAndroid Build Coastguard Worker best_len = std::max(best_len, PrefixLen(strings[i], strings[i + 1]));
458*795d594fSAndroid Build Coastguard Worker }
459*795d594fSAndroid Build Coastguard Worker best_len = std::min(best_len, kMaxPrefixLen);
460*795d594fSAndroid Build Coastguard Worker if (best_len >= kMinPrefixLen) {
461*795d594fSAndroid Build Coastguard Worker total_shared_prefix_bytes_ += best_len;
462*795d594fSAndroid Build Coastguard Worker }
463*795d594fSAndroid Build Coastguard Worker prefix_index_cost_ += kPrefixIndexCost;
464*795d594fSAndroid Build Coastguard Worker if (strings[i].length() < 64) {
465*795d594fSAndroid Build Coastguard Worker ++short_strings_;
466*795d594fSAndroid Build Coastguard Worker } else {
467*795d594fSAndroid Build Coastguard Worker ++long_strings_;
468*795d594fSAndroid Build Coastguard Worker }
469*795d594fSAndroid Build Coastguard Worker }
470*795d594fSAndroid Build Coastguard Worker total_prefix_index_cost_ += prefix_index_cost_;
471*795d594fSAndroid Build Coastguard Worker
472*795d594fSAndroid Build Coastguard Worker PrefixStrings prefix_strings;
473*795d594fSAndroid Build Coastguard Worker {
474*795d594fSAndroid Build Coastguard Worker PrefixStrings::Builder prefix_builder(&prefix_strings);
475*795d594fSAndroid Build Coastguard Worker prefix_builder.Build(strings);
476*795d594fSAndroid Build Coastguard Worker }
477*795d594fSAndroid Build Coastguard Worker Benchmark(prefix_strings, strings, &prefix_timings_);
478*795d594fSAndroid Build Coastguard Worker const size_t num_prefixes = prefix_strings.dictionary_.offsets_.size();
479*795d594fSAndroid Build Coastguard Worker total_num_prefixes_ += num_prefixes;
480*795d594fSAndroid Build Coastguard Worker total_prefix_table_ += num_prefixes * sizeof(prefix_strings.dictionary_.offsets_[0]);
481*795d594fSAndroid Build Coastguard Worker total_prefix_dict_ += prefix_strings.dictionary_.prefix_data_.size();
482*795d594fSAndroid Build Coastguard Worker
483*795d594fSAndroid Build Coastguard Worker {
484*795d594fSAndroid Build Coastguard Worker NormalStrings normal_strings;
485*795d594fSAndroid Build Coastguard Worker for (const std::string& s : strings) {
486*795d594fSAndroid Build Coastguard Worker normal_strings.AddString(s);
487*795d594fSAndroid Build Coastguard Worker }
488*795d594fSAndroid Build Coastguard Worker const uint64_t unique_string_data_bytes = normal_strings.chars_.size();
489*795d594fSAndroid Build Coastguard Worker total_unique_string_data_bytes_ += unique_string_data_bytes;
490*795d594fSAndroid Build Coastguard Worker total_prefix_savings_ += unique_string_data_bytes - prefix_strings.chars_.size() +
491*795d594fSAndroid Build Coastguard Worker prefix_index_cost_;
492*795d594fSAndroid Build Coastguard Worker Benchmark(normal_strings, strings, &normal_timings_);
493*795d594fSAndroid Build Coastguard Worker }
494*795d594fSAndroid Build Coastguard Worker }
495*795d594fSAndroid Build Coastguard Worker
496*795d594fSAndroid Build Coastguard Worker template <typename Strings>
Benchmark(const Strings & strings,const std::vector<std::string> & reference,StringTimings * timings)497*795d594fSAndroid Build Coastguard Worker void AnalyzeStrings::Benchmark(const Strings& strings,
498*795d594fSAndroid Build Coastguard Worker const std::vector<std::string>& reference,
499*795d594fSAndroid Build Coastguard Worker StringTimings* timings) {
500*795d594fSAndroid Build Coastguard Worker const size_t kIterations = 100;
501*795d594fSAndroid Build Coastguard Worker timings->num_comparisons_ += reference.size() * kIterations;
502*795d594fSAndroid Build Coastguard Worker
503*795d594fSAndroid Build Coastguard Worker uint64_t start = NanoTime();
504*795d594fSAndroid Build Coastguard Worker for (size_t j = 0; j < kIterations; ++j) {
505*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < reference.size(); ++i) {
506*795d594fSAndroid Build Coastguard Worker CHECK(strings.Equal(
507*795d594fSAndroid Build Coastguard Worker i,
508*795d594fSAndroid Build Coastguard Worker reinterpret_cast<const uint8_t*>(&reference[i][0]),
509*795d594fSAndroid Build Coastguard Worker reference[i].length()))
510*795d594fSAndroid Build Coastguard Worker << i << ": " << strings.GetString(i) << " vs " << reference[i];
511*795d594fSAndroid Build Coastguard Worker }
512*795d594fSAndroid Build Coastguard Worker }
513*795d594fSAndroid Build Coastguard Worker timings->time_equal_comparisons_ += NanoTime() - start;
514*795d594fSAndroid Build Coastguard Worker
515*795d594fSAndroid Build Coastguard Worker start = NanoTime();
516*795d594fSAndroid Build Coastguard Worker for (size_t j = 0; j < kIterations; ++j) {
517*795d594fSAndroid Build Coastguard Worker size_t count = 0u;
518*795d594fSAndroid Build Coastguard Worker for (size_t i = 0; i < reference.size(); ++i) {
519*795d594fSAndroid Build Coastguard Worker count += strings.Equal(
520*795d594fSAndroid Build Coastguard Worker reference.size() - 1 - i,
521*795d594fSAndroid Build Coastguard Worker reinterpret_cast<const uint8_t*>(&reference[i][0]),
522*795d594fSAndroid Build Coastguard Worker reference[i].length());
523*795d594fSAndroid Build Coastguard Worker }
524*795d594fSAndroid Build Coastguard Worker CHECK_LT(count, 2u);
525*795d594fSAndroid Build Coastguard Worker }
526*795d594fSAndroid Build Coastguard Worker timings->time_non_equal_comparisons_ += NanoTime() - start;
527*795d594fSAndroid Build Coastguard Worker }
528*795d594fSAndroid Build Coastguard Worker
529*795d594fSAndroid Build Coastguard Worker template void AnalyzeStrings::Benchmark(const PrefixStrings&,
530*795d594fSAndroid Build Coastguard Worker const std::vector<std::string>&,
531*795d594fSAndroid Build Coastguard Worker StringTimings* timings);
532*795d594fSAndroid Build Coastguard Worker template void AnalyzeStrings::Benchmark(const NormalStrings&,
533*795d594fSAndroid Build Coastguard Worker const std::vector<std::string>&,
534*795d594fSAndroid Build Coastguard Worker StringTimings* timings);
535*795d594fSAndroid Build Coastguard Worker
Dump(std::ostream & os) const536*795d594fSAndroid Build Coastguard Worker void StringTimings::Dump(std::ostream& os) const {
537*795d594fSAndroid Build Coastguard Worker const double comparisons = static_cast<double>(num_comparisons_);
538*795d594fSAndroid Build Coastguard Worker os << "Compare equal " << static_cast<double>(time_equal_comparisons_) / comparisons << "\n";
539*795d594fSAndroid Build Coastguard Worker os << "Compare not equal " << static_cast<double>(time_non_equal_comparisons_) / comparisons << "\n";
540*795d594fSAndroid Build Coastguard Worker }
541*795d594fSAndroid Build Coastguard Worker
Dump(std::ostream & os,uint64_t total_size) const542*795d594fSAndroid Build Coastguard Worker void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
543*795d594fSAndroid Build Coastguard Worker os << "Total string data bytes " << Percent(string_data_bytes_, total_size) << "\n";
544*795d594fSAndroid Build Coastguard Worker os << "Total unique string data bytes "
545*795d594fSAndroid Build Coastguard Worker << Percent(total_unique_string_data_bytes_, total_size) << "\n";
546*795d594fSAndroid Build Coastguard Worker os << "UTF-16 string data bytes " << Percent(wide_string_bytes_, total_size) << "\n";
547*795d594fSAndroid Build Coastguard Worker os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n";
548*795d594fSAndroid Build Coastguard Worker
549*795d594fSAndroid Build Coastguard Worker os << "Prefix string timings\n";
550*795d594fSAndroid Build Coastguard Worker prefix_timings_.Dump(os);
551*795d594fSAndroid Build Coastguard Worker os << "Normal string timings\n";
552*795d594fSAndroid Build Coastguard Worker normal_timings_.Dump(os);
553*795d594fSAndroid Build Coastguard Worker
554*795d594fSAndroid Build Coastguard Worker // Prefix based strings.
555*795d594fSAndroid Build Coastguard Worker os << "Total shared prefix bytes " << Percent(total_shared_prefix_bytes_, total_size) << "\n";
556*795d594fSAndroid Build Coastguard Worker os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
557*795d594fSAndroid Build Coastguard Worker os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
558*795d594fSAndroid Build Coastguard Worker os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
559*795d594fSAndroid Build Coastguard Worker int64_t net_savings = total_prefix_savings_;
560*795d594fSAndroid Build Coastguard Worker net_savings -= total_prefix_dict_;
561*795d594fSAndroid Build Coastguard Worker net_savings -= total_prefix_table_;
562*795d594fSAndroid Build Coastguard Worker net_savings -= total_prefix_index_cost_;
563*795d594fSAndroid Build Coastguard Worker os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
564*795d594fSAndroid Build Coastguard Worker os << "Prefix base savings " << Percent(total_prefix_savings_, total_size) << "\n";
565*795d594fSAndroid Build Coastguard Worker os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
566*795d594fSAndroid Build Coastguard Worker os << "Strings using prefix "
567*795d594fSAndroid Build Coastguard Worker << Percent(strings_used_prefixed_, total_prefix_index_cost_ / kPrefixIndexCost) << "\n";
568*795d594fSAndroid Build Coastguard Worker os << "Short strings " << Percent(short_strings_, short_strings_ + long_strings_) << "\n";
569*795d594fSAndroid Build Coastguard Worker if (verbose_level_ >= VerboseLevel::kEverything) {
570*795d594fSAndroid Build Coastguard Worker std::vector<std::pair<std::string, size_t>> pairs; // (prefixes_.begin(), prefixes_.end());
571*795d594fSAndroid Build Coastguard Worker // Sort lexicographically.
572*795d594fSAndroid Build Coastguard Worker std::sort(pairs.begin(), pairs.end());
573*795d594fSAndroid Build Coastguard Worker for (const auto& pair : pairs) {
574*795d594fSAndroid Build Coastguard Worker os << pair.first << " : " << pair.second << "\n";
575*795d594fSAndroid Build Coastguard Worker }
576*795d594fSAndroid Build Coastguard Worker }
577*795d594fSAndroid Build Coastguard Worker }
578*795d594fSAndroid Build Coastguard Worker
579*795d594fSAndroid Build Coastguard Worker } // namespace dexanalyze
580*795d594fSAndroid Build Coastguard Worker } // namespace art
581