xref: /aosp_15_r20/external/bsdiff/bsdiff_arguments.cc (revision a3a45f308bd90ef1a6e6a5e8fb92fe449b895909)
1*a3a45f30SXin Li // Copyright 2017 The Chromium OS Authors. All rights reserved.
2*a3a45f30SXin Li // Use of this source code is governed by a BSD-style license that can be
3*a3a45f30SXin Li // found in the LICENSE file.
4*a3a45f30SXin Li 
5*a3a45f30SXin Li #include "bsdiff/bsdiff_arguments.h"
6*a3a45f30SXin Li 
7*a3a45f30SXin Li #include <getopt.h>
8*a3a45f30SXin Li 
9*a3a45f30SXin Li #include <algorithm>
10*a3a45f30SXin Li #include <iostream>
11*a3a45f30SXin Li 
12*a3a45f30SXin Li #include "brotli/encode.h"
13*a3a45f30SXin Li 
14*a3a45f30SXin Li using std::endl;
15*a3a45f30SXin Li using std::string;
16*a3a45f30SXin Li 
17*a3a45f30SXin Li namespace {
18*a3a45f30SXin Li 
19*a3a45f30SXin Li // The name in string for the compression algorithms.
20*a3a45f30SXin Li constexpr char kNoCompressionString[] = "nocompression";
21*a3a45f30SXin Li constexpr char kBZ2String[] = "bz2";
22*a3a45f30SXin Li constexpr char kBrotliString[] = "brotli";
23*a3a45f30SXin Li 
24*a3a45f30SXin Li // The name in string for the bsdiff format.
25*a3a45f30SXin Li constexpr char kLegacyString[] = "legacy";
26*a3a45f30SXin Li constexpr char kBsdf2String[] = "bsdf2";
27*a3a45f30SXin Li constexpr char kBsdiff40String[] = "bsdiff40";
28*a3a45f30SXin Li constexpr char kEndsleyString[] = "endsley";
29*a3a45f30SXin Li 
30*a3a45f30SXin Li const struct option OPTIONS[] = {
31*a3a45f30SXin Li     {"format", required_argument, nullptr, 0},
32*a3a45f30SXin Li     {"minlen", required_argument, nullptr, 0},
33*a3a45f30SXin Li     {"type", required_argument, nullptr, 0},
34*a3a45f30SXin Li     {"brotli_quality", required_argument, nullptr, 0},
35*a3a45f30SXin Li     {nullptr, 0, nullptr, 0},
36*a3a45f30SXin Li };
37*a3a45f30SXin Li 
38*a3a45f30SXin Li const uint32_t kBrotliDefaultQuality = BROTLI_MAX_QUALITY;
39*a3a45f30SXin Li 
40*a3a45f30SXin Li }  // namespace
41*a3a45f30SXin Li 
42*a3a45f30SXin Li namespace bsdiff {
43*a3a45f30SXin Li 
compressor_types() const44*a3a45f30SXin Li std::vector<CompressorType> BsdiffArguments::compressor_types() const {
45*a3a45f30SXin Li   return std::vector<CompressorType>(compressor_types_.begin(),
46*a3a45f30SXin Li                                      compressor_types_.end());
47*a3a45f30SXin Li }
48*a3a45f30SXin Li 
IsValid() const49*a3a45f30SXin Li bool BsdiffArguments::IsValid() const {
50*a3a45f30SXin Li   if (compressor_types_.empty()) {
51*a3a45f30SXin Li     return false;
52*a3a45f30SXin Li   }
53*a3a45f30SXin Li 
54*a3a45f30SXin Li   if (IsCompressorSupported(CompressorType::kBrotli) &&
55*a3a45f30SXin Li       (brotli_quality_ < BROTLI_MIN_QUALITY ||
56*a3a45f30SXin Li        brotli_quality_ > BROTLI_MAX_QUALITY)) {
57*a3a45f30SXin Li     return false;
58*a3a45f30SXin Li   }
59*a3a45f30SXin Li 
60*a3a45f30SXin Li   if (format_ == BsdiffFormat::kLegacy) {
61*a3a45f30SXin Li     return compressor_types_.size() == 1 &&
62*a3a45f30SXin Li            IsCompressorSupported(CompressorType::kBZ2);
63*a3a45f30SXin Li   } else if (format_ == BsdiffFormat::kBsdf2) {
64*a3a45f30SXin Li     if (IsCompressorSupported(CompressorType::kNoCompression)) {
65*a3a45f30SXin Li       std::cerr << "no compression is not supported in Bsdf2 format\n";
66*a3a45f30SXin Li       return false;
67*a3a45f30SXin Li     }
68*a3a45f30SXin Li     return true;
69*a3a45f30SXin Li   } else if (format_ == BsdiffFormat::kEndsley) {
70*a3a45f30SXin Li     // Only one compressor is supported for this format.
71*a3a45f30SXin Li     return compressor_types_.size() == 1;
72*a3a45f30SXin Li   }
73*a3a45f30SXin Li   return false;
74*a3a45f30SXin Li }
75*a3a45f30SXin Li 
ParseCommandLine(int argc,char ** argv)76*a3a45f30SXin Li bool BsdiffArguments::ParseCommandLine(int argc, char** argv) {
77*a3a45f30SXin Li   int opt;
78*a3a45f30SXin Li   int option_index = 0;
79*a3a45f30SXin Li   while ((opt = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
80*a3a45f30SXin Li     if (opt != 0) {
81*a3a45f30SXin Li       return false;
82*a3a45f30SXin Li     }
83*a3a45f30SXin Li 
84*a3a45f30SXin Li     string name = OPTIONS[option_index].name;
85*a3a45f30SXin Li     if (name == "format") {
86*a3a45f30SXin Li       if (!ParseBsdiffFormat(optarg, &format_)) {
87*a3a45f30SXin Li         return false;
88*a3a45f30SXin Li       }
89*a3a45f30SXin Li     } else if (name == "minlen") {
90*a3a45f30SXin Li       if (!ParseMinLength(optarg, &min_length_)) {
91*a3a45f30SXin Li         return false;
92*a3a45f30SXin Li       }
93*a3a45f30SXin Li     } else if (name == "type") {
94*a3a45f30SXin Li       if (!ParseCompressorTypes(optarg, &compressor_types_)) {
95*a3a45f30SXin Li         return false;
96*a3a45f30SXin Li       }
97*a3a45f30SXin Li     } else if (name == "brotli_quality") {
98*a3a45f30SXin Li       if (!ParseQuality(optarg, &brotli_quality_, BROTLI_MIN_QUALITY,
99*a3a45f30SXin Li                         BROTLI_MAX_QUALITY)) {
100*a3a45f30SXin Li         return false;
101*a3a45f30SXin Li       }
102*a3a45f30SXin Li     } else {
103*a3a45f30SXin Li       std::cerr << "Unrecognized options: " << name << endl;
104*a3a45f30SXin Li       return false;
105*a3a45f30SXin Li     }
106*a3a45f30SXin Li   }
107*a3a45f30SXin Li 
108*a3a45f30SXin Li   // If quality is uninitialized for brotli, set it to default value.
109*a3a45f30SXin Li   if (format_ != BsdiffFormat::kLegacy &&
110*a3a45f30SXin Li       IsCompressorSupported(CompressorType::kBrotli) && brotli_quality_ == -1) {
111*a3a45f30SXin Li     brotli_quality_ = kBrotliDefaultQuality;
112*a3a45f30SXin Li   } else if (!IsCompressorSupported(CompressorType::kBrotli) &&
113*a3a45f30SXin Li              brotli_quality_ != -1) {
114*a3a45f30SXin Li     std::cerr << "Warning: Brotli quality is only used in the brotli"
115*a3a45f30SXin Li                  " compressor.\n";
116*a3a45f30SXin Li   }
117*a3a45f30SXin Li 
118*a3a45f30SXin Li   return true;
119*a3a45f30SXin Li }
120*a3a45f30SXin Li 
ParseCompressorTypes(const string & str,std::set<CompressorType> * types)121*a3a45f30SXin Li bool BsdiffArguments::ParseCompressorTypes(const string& str,
122*a3a45f30SXin Li                                            std::set<CompressorType>* types) {
123*a3a45f30SXin Li   types->clear();
124*a3a45f30SXin Li   // The expected types string is separated by ":", e.g. bz2:brotli
125*a3a45f30SXin Li   std::vector<std::string> type_list;
126*a3a45f30SXin Li   size_t base = 0;
127*a3a45f30SXin Li   size_t found;
128*a3a45f30SXin Li   while (true) {
129*a3a45f30SXin Li     found = str.find(":", base);
130*a3a45f30SXin Li     type_list.emplace_back(str, base, found - base);
131*a3a45f30SXin Li 
132*a3a45f30SXin Li     if (found == str.npos)
133*a3a45f30SXin Li       break;
134*a3a45f30SXin Li     base = found + 1;
135*a3a45f30SXin Li   }
136*a3a45f30SXin Li 
137*a3a45f30SXin Li   for (auto& type : type_list) {
138*a3a45f30SXin Li     std::transform(type.begin(), type.end(), type.begin(), ::tolower);
139*a3a45f30SXin Li     if (type == kNoCompressionString) {
140*a3a45f30SXin Li       types->emplace(CompressorType::kNoCompression);
141*a3a45f30SXin Li     } else if (type == kBZ2String) {
142*a3a45f30SXin Li       types->emplace(CompressorType::kBZ2);
143*a3a45f30SXin Li     } else if (type == kBrotliString) {
144*a3a45f30SXin Li       types->emplace(CompressorType::kBrotli);
145*a3a45f30SXin Li     } else {
146*a3a45f30SXin Li       std::cerr << "Failed to parse compressor type in " << str << endl;
147*a3a45f30SXin Li       return false;
148*a3a45f30SXin Li     }
149*a3a45f30SXin Li   }
150*a3a45f30SXin Li 
151*a3a45f30SXin Li   return true;
152*a3a45f30SXin Li }
153*a3a45f30SXin Li 
ParseMinLength(const string & str,size_t * len)154*a3a45f30SXin Li bool BsdiffArguments::ParseMinLength(const string& str, size_t* len) {
155*a3a45f30SXin Li   errno = 0;
156*a3a45f30SXin Li   char* end;
157*a3a45f30SXin Li   const char* s = str.c_str();
158*a3a45f30SXin Li   long result = strtol(s, &end, 10);
159*a3a45f30SXin Li   if (errno != 0 || s == end || *end != '\0') {
160*a3a45f30SXin Li     return false;
161*a3a45f30SXin Li   }
162*a3a45f30SXin Li 
163*a3a45f30SXin Li   if (result < 0) {
164*a3a45f30SXin Li     std::cerr << "Minimum length must be non-negative: " << str << endl;
165*a3a45f30SXin Li     return false;
166*a3a45f30SXin Li   }
167*a3a45f30SXin Li 
168*a3a45f30SXin Li   *len = result;
169*a3a45f30SXin Li   return true;
170*a3a45f30SXin Li }
171*a3a45f30SXin Li 
ParseBsdiffFormat(const string & str,BsdiffFormat * format)172*a3a45f30SXin Li bool BsdiffArguments::ParseBsdiffFormat(const string& str,
173*a3a45f30SXin Li                                         BsdiffFormat* format) {
174*a3a45f30SXin Li   string format_string = str;
175*a3a45f30SXin Li   std::transform(format_string.begin(), format_string.end(),
176*a3a45f30SXin Li                  format_string.begin(), ::tolower);
177*a3a45f30SXin Li   if (format_string == kLegacyString || format_string == kBsdiff40String) {
178*a3a45f30SXin Li     *format = BsdiffFormat::kLegacy;
179*a3a45f30SXin Li     return true;
180*a3a45f30SXin Li   } else if (format_string == kBsdf2String) {
181*a3a45f30SXin Li     *format = BsdiffFormat::kBsdf2;
182*a3a45f30SXin Li     return true;
183*a3a45f30SXin Li   } else if (format_string == kEndsleyString) {
184*a3a45f30SXin Li     *format = BsdiffFormat::kEndsley;
185*a3a45f30SXin Li     return true;
186*a3a45f30SXin Li   }
187*a3a45f30SXin Li   std::cerr << "Failed to parse bsdiff format in " << str << endl;
188*a3a45f30SXin Li   return false;
189*a3a45f30SXin Li }
190*a3a45f30SXin Li 
ParseQuality(const string & str,int * quality,int min,int max)191*a3a45f30SXin Li bool BsdiffArguments::ParseQuality(const string& str,
192*a3a45f30SXin Li                                    int* quality,
193*a3a45f30SXin Li                                    int min,
194*a3a45f30SXin Li                                    int max) {
195*a3a45f30SXin Li   errno = 0;
196*a3a45f30SXin Li   char* end;
197*a3a45f30SXin Li   const char* s = str.c_str();
198*a3a45f30SXin Li   long result = strtol(s, &end, 10);
199*a3a45f30SXin Li   if (errno != 0 || s == end || *end != '\0') {
200*a3a45f30SXin Li     return false;
201*a3a45f30SXin Li   }
202*a3a45f30SXin Li 
203*a3a45f30SXin Li   if (result < min || result > max) {
204*a3a45f30SXin Li     std::cerr << "Compression quality out of range " << str << endl;
205*a3a45f30SXin Li     return false;
206*a3a45f30SXin Li   }
207*a3a45f30SXin Li 
208*a3a45f30SXin Li   *quality = result;
209*a3a45f30SXin Li   return true;
210*a3a45f30SXin Li }
211*a3a45f30SXin Li 
IsCompressorSupported(CompressorType type) const212*a3a45f30SXin Li bool BsdiffArguments::IsCompressorSupported(CompressorType type) const {
213*a3a45f30SXin Li   return compressor_types_.find(type) != compressor_types_.end();
214*a3a45f30SXin Li }
215*a3a45f30SXin Li 
216*a3a45f30SXin Li }  // namespace bsdiff
217