OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Histogram is an object that aggregates statistics, and can summarize them in | 5 // Histogram is an object that aggregates statistics, and can summarize them in |
6 // various forms, including ASCII graphical, HTML, and numerically (as a | 6 // various forms, including ASCII graphical, HTML, and numerically (as a |
7 // vector of numbers corresponding to each of the aggregating buckets). | 7 // vector of numbers corresponding to each of the aggregating buckets). |
8 // See header file for details and examples. | 8 // See header file for details and examples. |
9 | 9 |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
268 int32_t flags) { | 268 int32_t flags) { |
269 return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, | 269 return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, |
270 flags); | 270 flags); |
271 } | 271 } |
272 | 272 |
273 std::unique_ptr<HistogramBase> Histogram::PersistentCreate( | 273 std::unique_ptr<HistogramBase> Histogram::PersistentCreate( |
274 const std::string& name, | 274 const std::string& name, |
275 Sample minimum, | 275 Sample minimum, |
276 Sample maximum, | 276 Sample maximum, |
277 const BucketRanges* ranges, | 277 const BucketRanges* ranges, |
278 HistogramBase::AtomicCount* counts, | 278 const DelayedPersistentAllocation& counts, |
279 HistogramBase::AtomicCount* logged_counts, | 279 const DelayedPersistentAllocation& logged_counts, |
280 uint32_t counts_size, | |
281 HistogramSamples::Metadata* meta, | 280 HistogramSamples::Metadata* meta, |
282 HistogramSamples::Metadata* logged_meta) { | 281 HistogramSamples::Metadata* logged_meta) { |
283 return WrapUnique(new Histogram(name, minimum, maximum, ranges, counts, | 282 return WrapUnique(new Histogram(name, minimum, maximum, ranges, counts, |
284 logged_counts, counts_size, meta, | 283 logged_counts, meta, logged_meta)); |
285 logged_meta)); | |
286 } | 284 } |
287 | 285 |
288 // Calculate what range of values are held in each bucket. | 286 // Calculate what range of values are held in each bucket. |
289 // We have to be careful that we don't pick a ratio between starting points in | 287 // We have to be careful that we don't pick a ratio between starting points in |
290 // consecutive buckets that is sooo small, that the integer bounds are the same | 288 // consecutive buckets that is sooo small, that the integer bounds are the same |
291 // (effectively making one bucket get no values). We need to avoid: | 289 // (effectively making one bucket get no values). We need to avoid: |
292 // ranges(i) == ranges(i + 1) | 290 // ranges(i) == ranges(i + 1) |
293 // To avoid that, we just do a fine-grained bucket width as far as we need to | 291 // To avoid that, we just do a fine-grained bucket width as far as we need to |
294 // until we get a ratio that moves us along at least 2 units at a time. From | 292 // until we get a ratio that moves us along at least 2 units at a time. From |
295 // that bucket onward we do use the exponential growth of buckets. | 293 // that bucket onward we do use the exponential growth of buckets. |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
505 declared_min_(minimum), | 503 declared_min_(minimum), |
506 declared_max_(maximum) { | 504 declared_max_(maximum) { |
507 if (ranges) | 505 if (ranges) |
508 samples_.reset(new SampleVector(HashMetricName(name), ranges)); | 506 samples_.reset(new SampleVector(HashMetricName(name), ranges)); |
509 } | 507 } |
510 | 508 |
511 Histogram::Histogram(const std::string& name, | 509 Histogram::Histogram(const std::string& name, |
512 Sample minimum, | 510 Sample minimum, |
513 Sample maximum, | 511 Sample maximum, |
514 const BucketRanges* ranges, | 512 const BucketRanges* ranges, |
515 HistogramBase::AtomicCount* counts, | 513 const DelayedPersistentAllocation& counts, |
516 HistogramBase::AtomicCount* logged_counts, | 514 const DelayedPersistentAllocation& logged_counts, |
517 uint32_t counts_size, | |
518 HistogramSamples::Metadata* meta, | 515 HistogramSamples::Metadata* meta, |
519 HistogramSamples::Metadata* logged_meta) | 516 HistogramSamples::Metadata* logged_meta) |
520 : HistogramBase(name), | 517 : HistogramBase(name), |
521 bucket_ranges_(ranges), | 518 bucket_ranges_(ranges), |
522 declared_min_(minimum), | 519 declared_min_(minimum), |
523 declared_max_(maximum) { | 520 declared_max_(maximum) { |
524 if (ranges) { | 521 if (ranges) { |
525 samples_.reset(new SampleVector(HashMetricName(name), | 522 samples_.reset( |
526 counts, counts_size, meta, ranges)); | 523 new PersistentSampleVector(HashMetricName(name), ranges, meta, counts)); |
Alexei Svitkine (slow)
2017/04/20 19:56:10
Wait, why is this switching to PersistentSampleVec
bcwhite
2017/04/20 21:18:07
Previously, the SampleVector had two ctors, one th
Alexei Svitkine (slow)
2017/04/20 22:11:34
Makes sense - thanks for the explanation!
| |
527 logged_samples_.reset(new SampleVector(samples_->id(), logged_counts, | 524 logged_samples_.reset(new PersistentSampleVector( |
528 counts_size, logged_meta, ranges)); | 525 samples_->id(), ranges, logged_meta, logged_counts)); |
529 } | 526 } |
530 } | 527 } |
531 | 528 |
532 Histogram::~Histogram() { | 529 Histogram::~Histogram() { |
533 } | 530 } |
534 | 531 |
535 bool Histogram::PrintEmptyBucket(uint32_t index) const { | 532 bool Histogram::PrintEmptyBucket(uint32_t index) const { |
536 return true; | 533 return true; |
537 } | 534 } |
538 | 535 |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
649 double current_size = GetBucketSize(current, i); | 646 double current_size = GetBucketSize(current, i); |
650 if (graph_it) | 647 if (graph_it) |
651 WriteAsciiBucketGraph(current_size, max_size, output); | 648 WriteAsciiBucketGraph(current_size, max_size, output); |
652 WriteAsciiBucketContext(past, current, remaining, i, output); | 649 WriteAsciiBucketContext(past, current, remaining, i, output); |
653 output->append(newline); | 650 output->append(newline); |
654 past += current; | 651 past += current; |
655 } | 652 } |
656 DCHECK_EQ(sample_count, past); | 653 DCHECK_EQ(sample_count, past); |
657 } | 654 } |
658 | 655 |
659 double Histogram::GetPeakBucketSize(const SampleVector& samples) const { | 656 double Histogram::GetPeakBucketSize(const SampleVectorBase& samples) const { |
660 double max = 0; | 657 double max = 0; |
661 for (uint32_t i = 0; i < bucket_count() ; ++i) { | 658 for (uint32_t i = 0; i < bucket_count() ; ++i) { |
662 double current_size = GetBucketSize(samples.GetCountAtIndex(i), i); | 659 double current_size = GetBucketSize(samples.GetCountAtIndex(i), i); |
663 if (current_size > max) | 660 if (current_size > max) |
664 max = current_size; | 661 max = current_size; |
665 } | 662 } |
666 return max; | 663 return max; |
667 } | 664 } |
668 | 665 |
669 void Histogram::WriteAsciiHeader(const SampleVector& samples, | 666 void Histogram::WriteAsciiHeader(const SampleVectorBase& samples, |
670 Count sample_count, | 667 Count sample_count, |
671 std::string* output) const { | 668 std::string* output) const { |
672 StringAppendF(output, | 669 StringAppendF(output, |
673 "Histogram: %s recorded %d samples", | 670 "Histogram: %s recorded %d samples", |
674 histogram_name().c_str(), | 671 histogram_name().c_str(), |
675 sample_count); | 672 sample_count); |
676 if (sample_count == 0) { | 673 if (sample_count == 0) { |
677 DCHECK_EQ(samples.sum(), 0); | 674 DCHECK_EQ(samples.sum(), 0); |
678 } else { | 675 } else { |
679 double mean = static_cast<float>(samples.sum()) / sample_count; | 676 double mean = static_cast<float>(samples.sum()) / sample_count; |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
808 int32_t flags) { | 805 int32_t flags) { |
809 return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, | 806 return FactoryTimeGet(std::string(name), minimum, maximum, bucket_count, |
810 flags); | 807 flags); |
811 } | 808 } |
812 | 809 |
813 std::unique_ptr<HistogramBase> LinearHistogram::PersistentCreate( | 810 std::unique_ptr<HistogramBase> LinearHistogram::PersistentCreate( |
814 const std::string& name, | 811 const std::string& name, |
815 Sample minimum, | 812 Sample minimum, |
816 Sample maximum, | 813 Sample maximum, |
817 const BucketRanges* ranges, | 814 const BucketRanges* ranges, |
818 HistogramBase::AtomicCount* counts, | 815 const DelayedPersistentAllocation& counts, |
819 HistogramBase::AtomicCount* logged_counts, | 816 const DelayedPersistentAllocation& logged_counts, |
820 uint32_t counts_size, | |
821 HistogramSamples::Metadata* meta, | 817 HistogramSamples::Metadata* meta, |
822 HistogramSamples::Metadata* logged_meta) { | 818 HistogramSamples::Metadata* logged_meta) { |
823 return WrapUnique(new LinearHistogram(name, minimum, maximum, ranges, | 819 return WrapUnique(new LinearHistogram(name, minimum, maximum, ranges, counts, |
824 counts, logged_counts, | 820 logged_counts, meta, logged_meta)); |
825 counts_size, meta, logged_meta)); | |
826 } | 821 } |
827 | 822 |
828 HistogramBase* LinearHistogram::FactoryGetWithRangeDescription( | 823 HistogramBase* LinearHistogram::FactoryGetWithRangeDescription( |
829 const std::string& name, | 824 const std::string& name, |
830 Sample minimum, | 825 Sample minimum, |
831 Sample maximum, | 826 Sample maximum, |
832 uint32_t bucket_count, | 827 uint32_t bucket_count, |
833 int32_t flags, | 828 int32_t flags, |
834 const DescriptionPair descriptions[]) { | 829 const DescriptionPair descriptions[]) { |
835 bool valid_arguments = Histogram::InspectConstructionArguments( | 830 bool valid_arguments = Histogram::InspectConstructionArguments( |
836 name, &minimum, &maximum, &bucket_count); | 831 name, &minimum, &maximum, &bucket_count); |
837 DCHECK(valid_arguments); | 832 DCHECK(valid_arguments); |
838 | 833 |
839 return Factory(name, minimum, maximum, bucket_count, flags, descriptions) | 834 return Factory(name, minimum, maximum, bucket_count, flags, descriptions) |
840 .Build(); | 835 .Build(); |
841 } | 836 } |
842 | 837 |
843 HistogramType LinearHistogram::GetHistogramType() const { | 838 HistogramType LinearHistogram::GetHistogramType() const { |
844 return LINEAR_HISTOGRAM; | 839 return LINEAR_HISTOGRAM; |
845 } | 840 } |
846 | 841 |
847 LinearHistogram::LinearHistogram(const std::string& name, | 842 LinearHistogram::LinearHistogram(const std::string& name, |
848 Sample minimum, | 843 Sample minimum, |
849 Sample maximum, | 844 Sample maximum, |
850 const BucketRanges* ranges) | 845 const BucketRanges* ranges) |
851 : Histogram(name, minimum, maximum, ranges) { | 846 : Histogram(name, minimum, maximum, ranges) { |
852 } | 847 } |
853 | 848 |
854 LinearHistogram::LinearHistogram(const std::string& name, | 849 LinearHistogram::LinearHistogram( |
855 Sample minimum, | 850 const std::string& name, |
856 Sample maximum, | 851 Sample minimum, |
857 const BucketRanges* ranges, | 852 Sample maximum, |
858 HistogramBase::AtomicCount* counts, | 853 const BucketRanges* ranges, |
859 HistogramBase::AtomicCount* logged_counts, | 854 const DelayedPersistentAllocation& counts, |
860 uint32_t counts_size, | 855 const DelayedPersistentAllocation& logged_counts, |
861 HistogramSamples::Metadata* meta, | 856 HistogramSamples::Metadata* meta, |
862 HistogramSamples::Metadata* logged_meta) | 857 HistogramSamples::Metadata* logged_meta) |
863 : Histogram(name, minimum, maximum, ranges, counts, logged_counts, | 858 : Histogram(name, |
864 counts_size, meta, logged_meta) {} | 859 minimum, |
860 maximum, | |
861 ranges, | |
862 counts, | |
863 logged_counts, | |
864 meta, | |
865 logged_meta) {} | |
865 | 866 |
866 double LinearHistogram::GetBucketSize(Count current, uint32_t i) const { | 867 double LinearHistogram::GetBucketSize(Count current, uint32_t i) const { |
867 DCHECK_GT(ranges(i + 1), ranges(i)); | 868 DCHECK_GT(ranges(i + 1), ranges(i)); |
868 // Adjacent buckets with different widths would have "surprisingly" many (few) | 869 // Adjacent buckets with different widths would have "surprisingly" many (few) |
869 // samples in a histogram if we didn't normalize this way. | 870 // samples in a histogram if we didn't normalize this way. |
870 double denominator = ranges(i + 1) - ranges(i); | 871 double denominator = ranges(i + 1) - ranges(i); |
871 return current/denominator; | 872 return current/denominator; |
872 } | 873 } |
873 | 874 |
874 const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const { | 875 const std::string LinearHistogram::GetAsciiBucketRange(uint32_t i) const { |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
954 return Factory(name, flags).Build(); | 955 return Factory(name, flags).Build(); |
955 } | 956 } |
956 | 957 |
957 HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) { | 958 HistogramBase* BooleanHistogram::FactoryGet(const char* name, int32_t flags) { |
958 return FactoryGet(std::string(name), flags); | 959 return FactoryGet(std::string(name), flags); |
959 } | 960 } |
960 | 961 |
961 std::unique_ptr<HistogramBase> BooleanHistogram::PersistentCreate( | 962 std::unique_ptr<HistogramBase> BooleanHistogram::PersistentCreate( |
962 const std::string& name, | 963 const std::string& name, |
963 const BucketRanges* ranges, | 964 const BucketRanges* ranges, |
964 HistogramBase::AtomicCount* counts, | 965 const DelayedPersistentAllocation& counts, |
965 HistogramBase::AtomicCount* logged_counts, | 966 const DelayedPersistentAllocation& logged_counts, |
966 HistogramSamples::Metadata* meta, | 967 HistogramSamples::Metadata* meta, |
967 HistogramSamples::Metadata* logged_meta) { | 968 HistogramSamples::Metadata* logged_meta) { |
968 return WrapUnique(new BooleanHistogram( | 969 return WrapUnique(new BooleanHistogram(name, ranges, counts, logged_counts, |
969 name, ranges, counts, logged_counts, meta, logged_meta)); | 970 meta, logged_meta)); |
970 } | 971 } |
971 | 972 |
972 HistogramType BooleanHistogram::GetHistogramType() const { | 973 HistogramType BooleanHistogram::GetHistogramType() const { |
973 return BOOLEAN_HISTOGRAM; | 974 return BOOLEAN_HISTOGRAM; |
974 } | 975 } |
975 | 976 |
976 BooleanHistogram::BooleanHistogram(const std::string& name, | 977 BooleanHistogram::BooleanHistogram(const std::string& name, |
977 const BucketRanges* ranges) | 978 const BucketRanges* ranges) |
978 : LinearHistogram(name, 1, 2, ranges) {} | 979 : LinearHistogram(name, 1, 2, ranges) {} |
979 | 980 |
980 BooleanHistogram::BooleanHistogram(const std::string& name, | 981 BooleanHistogram::BooleanHistogram( |
981 const BucketRanges* ranges, | 982 const std::string& name, |
982 HistogramBase::AtomicCount* counts, | 983 const BucketRanges* ranges, |
983 HistogramBase::AtomicCount* logged_counts, | 984 const DelayedPersistentAllocation& counts, |
984 HistogramSamples::Metadata* meta, | 985 const DelayedPersistentAllocation& logged_counts, |
985 HistogramSamples::Metadata* logged_meta) | 986 HistogramSamples::Metadata* meta, |
986 : LinearHistogram(name, 1, 2, ranges, counts, logged_counts, 2, meta, | 987 HistogramSamples::Metadata* logged_meta) |
988 : LinearHistogram(name, | |
989 1, | |
990 2, | |
991 ranges, | |
992 counts, | |
993 logged_counts, | |
994 meta, | |
987 logged_meta) {} | 995 logged_meta) {} |
988 | 996 |
989 HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) { | 997 HistogramBase* BooleanHistogram::DeserializeInfoImpl(PickleIterator* iter) { |
990 std::string histogram_name; | 998 std::string histogram_name; |
991 int flags; | 999 int flags; |
992 int declared_min; | 1000 int declared_min; |
993 int declared_max; | 1001 int declared_max; |
994 uint32_t bucket_count; | 1002 uint32_t bucket_count; |
995 uint32_t range_checksum; | 1003 uint32_t range_checksum; |
996 | 1004 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1061 HistogramBase* CustomHistogram::FactoryGet( | 1069 HistogramBase* CustomHistogram::FactoryGet( |
1062 const char* name, | 1070 const char* name, |
1063 const std::vector<Sample>& custom_ranges, | 1071 const std::vector<Sample>& custom_ranges, |
1064 int32_t flags) { | 1072 int32_t flags) { |
1065 return FactoryGet(std::string(name), custom_ranges, flags); | 1073 return FactoryGet(std::string(name), custom_ranges, flags); |
1066 } | 1074 } |
1067 | 1075 |
1068 std::unique_ptr<HistogramBase> CustomHistogram::PersistentCreate( | 1076 std::unique_ptr<HistogramBase> CustomHistogram::PersistentCreate( |
1069 const std::string& name, | 1077 const std::string& name, |
1070 const BucketRanges* ranges, | 1078 const BucketRanges* ranges, |
1071 HistogramBase::AtomicCount* counts, | 1079 const DelayedPersistentAllocation& counts, |
1072 HistogramBase::AtomicCount* logged_counts, | 1080 const DelayedPersistentAllocation& logged_counts, |
1073 uint32_t counts_size, | |
1074 HistogramSamples::Metadata* meta, | 1081 HistogramSamples::Metadata* meta, |
1075 HistogramSamples::Metadata* logged_meta) { | 1082 HistogramSamples::Metadata* logged_meta) { |
1076 return WrapUnique(new CustomHistogram( | 1083 return WrapUnique(new CustomHistogram(name, ranges, counts, logged_counts, |
1077 name, ranges, counts, logged_counts, counts_size, meta, logged_meta)); | 1084 meta, logged_meta)); |
1078 } | 1085 } |
1079 | 1086 |
1080 HistogramType CustomHistogram::GetHistogramType() const { | 1087 HistogramType CustomHistogram::GetHistogramType() const { |
1081 return CUSTOM_HISTOGRAM; | 1088 return CUSTOM_HISTOGRAM; |
1082 } | 1089 } |
1083 | 1090 |
1084 // static | 1091 // static |
1085 std::vector<Sample> CustomHistogram::ArrayToCustomRanges( | 1092 std::vector<Sample> CustomHistogram::ArrayToCustomRanges( |
1086 const Sample* values, uint32_t num_values) { | 1093 const Sample* values, uint32_t num_values) { |
1087 std::vector<Sample> all_values; | 1094 std::vector<Sample> all_values; |
1088 for (uint32_t i = 0; i < num_values; ++i) { | 1095 for (uint32_t i = 0; i < num_values; ++i) { |
1089 Sample value = values[i]; | 1096 Sample value = values[i]; |
1090 all_values.push_back(value); | 1097 all_values.push_back(value); |
1091 | 1098 |
1092 // Ensure that a guard bucket is added. If we end up with duplicate | 1099 // Ensure that a guard bucket is added. If we end up with duplicate |
1093 // values, FactoryGet will take care of removing them. | 1100 // values, FactoryGet will take care of removing them. |
1094 all_values.push_back(value + 1); | 1101 all_values.push_back(value + 1); |
1095 } | 1102 } |
1096 return all_values; | 1103 return all_values; |
1097 } | 1104 } |
1098 | 1105 |
1099 CustomHistogram::CustomHistogram(const std::string& name, | 1106 CustomHistogram::CustomHistogram(const std::string& name, |
1100 const BucketRanges* ranges) | 1107 const BucketRanges* ranges) |
1101 : Histogram(name, | 1108 : Histogram(name, |
1102 ranges->range(1), | 1109 ranges->range(1), |
1103 ranges->range(ranges->bucket_count() - 1), | 1110 ranges->range(ranges->bucket_count() - 1), |
1104 ranges) {} | 1111 ranges) {} |
1105 | 1112 |
1106 CustomHistogram::CustomHistogram(const std::string& name, | 1113 CustomHistogram::CustomHistogram( |
1107 const BucketRanges* ranges, | 1114 const std::string& name, |
1108 HistogramBase::AtomicCount* counts, | 1115 const BucketRanges* ranges, |
1109 HistogramBase::AtomicCount* logged_counts, | 1116 const DelayedPersistentAllocation& counts, |
1110 uint32_t counts_size, | 1117 const DelayedPersistentAllocation& logged_counts, |
1111 HistogramSamples::Metadata* meta, | 1118 HistogramSamples::Metadata* meta, |
1112 HistogramSamples::Metadata* logged_meta) | 1119 HistogramSamples::Metadata* logged_meta) |
1113 : Histogram(name, | 1120 : Histogram(name, |
1114 ranges->range(1), | 1121 ranges->range(1), |
1115 ranges->range(ranges->bucket_count() - 1), | 1122 ranges->range(ranges->bucket_count() - 1), |
1116 ranges, | 1123 ranges, |
1117 counts, | 1124 counts, |
1118 logged_counts, | 1125 logged_counts, |
1119 counts_size, | |
1120 meta, | 1126 meta, |
1121 logged_meta) {} | 1127 logged_meta) {} |
1122 | 1128 |
1123 bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const { | 1129 bool CustomHistogram::SerializeInfoImpl(Pickle* pickle) const { |
1124 if (!Histogram::SerializeInfoImpl(pickle)) | 1130 if (!Histogram::SerializeInfoImpl(pickle)) |
1125 return false; | 1131 return false; |
1126 | 1132 |
1127 // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't | 1133 // Serialize ranges. First and last ranges are alwasy 0 and INT_MAX, so don't |
1128 // write them. | 1134 // write them. |
1129 for (uint32_t i = 1; i < bucket_ranges()->bucket_count(); ++i) { | 1135 for (uint32_t i = 1; i < bucket_ranges()->bucket_count(); ++i) { |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1178 Sample sample = custom_ranges[i]; | 1184 Sample sample = custom_ranges[i]; |
1179 if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1) | 1185 if (sample < 0 || sample > HistogramBase::kSampleType_MAX - 1) |
1180 return false; | 1186 return false; |
1181 if (sample != 0) | 1187 if (sample != 0) |
1182 has_valid_range = true; | 1188 has_valid_range = true; |
1183 } | 1189 } |
1184 return has_valid_range; | 1190 return has_valid_range; |
1185 } | 1191 } |
1186 | 1192 |
1187 } // namespace base | 1193 } // namespace base |
OLD | NEW |