1 // MethodProps.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../Common/StringToInt.h"
6 
7 #include "MethodProps.h"
8 
9 using namespace NWindows;
10 
StringToBool(const UString & s,bool & res)11 bool StringToBool(const UString &s, bool &res)
12 {
13   if (s.IsEmpty() || s == L"+" || StringsAreEqualNoCase_Ascii(s, "ON"))
14   {
15     res = true;
16     return true;
17   }
18   if (s == L"-" || StringsAreEqualNoCase_Ascii(s, "OFF"))
19   {
20     res = false;
21     return true;
22   }
23   return false;
24 }
25 
PROPVARIANT_to_bool(const PROPVARIANT & prop,bool & dest)26 HRESULT PROPVARIANT_to_bool(const PROPVARIANT &prop, bool &dest)
27 {
28   switch (prop.vt)
29   {
30     case VT_EMPTY: dest = true; return S_OK;
31     case VT_BOOL: dest = (prop.boolVal != VARIANT_FALSE); return S_OK;
32     case VT_BSTR: return StringToBool(prop.bstrVal, dest) ? S_OK : E_INVALIDARG;
33   }
34   return E_INVALIDARG;
35 }
36 
ParseStringToUInt32(const UString & srcString,UInt32 & number)37 unsigned ParseStringToUInt32(const UString &srcString, UInt32 &number)
38 {
39   const wchar_t *start = srcString;
40   const wchar_t *end;
41   number = ConvertStringToUInt32(start, &end);
42   return (unsigned)(end - start);
43 }
44 
ParsePropToUInt32(const UString & name,const PROPVARIANT & prop,UInt32 & resValue)45 HRESULT ParsePropToUInt32(const UString &name, const PROPVARIANT &prop, UInt32 &resValue)
46 {
47   // =VT_UI4
48   // =VT_EMPTY
49   // {stringUInt32}=VT_EMPTY
50 
51   if (prop.vt == VT_UI4)
52   {
53     if (!name.IsEmpty())
54       return E_INVALIDARG;
55     resValue = prop.ulVal;
56     return S_OK;
57   }
58   if (prop.vt != VT_EMPTY)
59     return E_INVALIDARG;
60   if (name.IsEmpty())
61     return S_OK;
62   UInt32 v;
63   if (ParseStringToUInt32(name, v) != name.Len())
64     return E_INVALIDARG;
65   resValue = v;
66   return S_OK;
67 }
68 
ParseMtProp(const UString & name,const PROPVARIANT & prop,UInt32 defaultNumThreads,UInt32 & numThreads)69 HRESULT ParseMtProp(const UString &name, const PROPVARIANT &prop, UInt32 defaultNumThreads, UInt32 &numThreads)
70 {
71   if (name.IsEmpty())
72   {
73     switch (prop.vt)
74     {
75       case VT_UI4:
76         numThreads = prop.ulVal;
77         break;
78       default:
79       {
80         bool val;
81         RINOK(PROPVARIANT_to_bool(prop, val));
82         numThreads = (val ? defaultNumThreads : 1);
83         break;
84       }
85     }
86     return S_OK;
87   }
88   if (prop.vt != VT_EMPTY)
89     return E_INVALIDARG;
90   return ParsePropToUInt32(name, prop, numThreads);
91 }
92 
StringToDictSize(const UString & s,UInt32 & dicSize)93 static HRESULT StringToDictSize(const UString &s, UInt32 &dicSize)
94 {
95   const wchar_t *end;
96   UInt32 number = ConvertStringToUInt32(s, &end);
97   unsigned numDigits = (unsigned)(end - s);
98   if (numDigits == 0 || s.Len() > numDigits + 1)
99     return E_INVALIDARG;
100   const unsigned kLogDictSizeLimit = 32;
101   if (s.Len() == numDigits)
102   {
103     if (number >= kLogDictSizeLimit)
104       return E_INVALIDARG;
105     dicSize = (UInt32)1 << (unsigned)number;
106     return S_OK;
107   }
108   unsigned numBits;
109   switch (MyCharLower_Ascii(s[numDigits]))
110   {
111     case 'b': dicSize = number; return S_OK;
112     case 'k': numBits = 10; break;
113     case 'm': numBits = 20; break;
114     case 'g': numBits = 30; break;
115     default: return E_INVALIDARG;
116   }
117   if (number >= ((UInt32)1 << (kLogDictSizeLimit - numBits)))
118     return E_INVALIDARG;
119   dicSize = number << numBits;
120   return S_OK;
121 }
122 
PROPVARIANT_to_DictSize(const PROPVARIANT & prop,UInt32 & resValue)123 static HRESULT PROPVARIANT_to_DictSize(const PROPVARIANT &prop, UInt32 &resValue)
124 {
125   if (prop.vt == VT_UI4)
126   {
127     UInt32 v = prop.ulVal;
128     if (v >= 32)
129       return E_INVALIDARG;
130     resValue = (UInt32)1 << v;
131     return S_OK;
132   }
133   if (prop.vt == VT_BSTR)
134     return StringToDictSize(prop.bstrVal, resValue);
135   return E_INVALIDARG;
136 }
137 
AddProp32(PROPID propid,UInt32 level)138 void CProps::AddProp32(PROPID propid, UInt32 level)
139 {
140   CProp prop;
141   prop.IsOptional = true;
142   prop.Id = propid;
143   prop.Value = (UInt32)level;
144   Props.Add(prop);
145 }
146 
147 class CCoderProps
148 {
149   PROPID *_propIDs;
150   NCOM::CPropVariant *_props;
151   unsigned _numProps;
152   unsigned _numPropsMax;
153 public:
CCoderProps(unsigned numPropsMax)154   CCoderProps(unsigned numPropsMax)
155   {
156     _numPropsMax = numPropsMax;
157     _numProps = 0;
158     _propIDs = new PROPID[numPropsMax];
159     _props = new NCOM::CPropVariant[numPropsMax];
160   }
~CCoderProps()161   ~CCoderProps()
162   {
163     delete []_propIDs;
164     delete []_props;
165   }
166   void AddProp(const CProp &prop);
SetProps(ICompressSetCoderProperties * setCoderProperties)167   HRESULT SetProps(ICompressSetCoderProperties *setCoderProperties)
168   {
169     return setCoderProperties->SetCoderProperties(_propIDs, _props, _numProps);
170   }
171 };
172 
AddProp(const CProp & prop)173 void CCoderProps::AddProp(const CProp &prop)
174 {
175   if (_numProps >= _numPropsMax)
176     throw 1;
177   _propIDs[_numProps] = prop.Id;
178   _props[_numProps] = prop.Value;
179   _numProps++;
180 }
181 
SetCoderProps(ICompressSetCoderProperties * scp,const UInt64 * dataSizeReduce) const182 HRESULT CProps::SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const
183 {
184   CCoderProps coderProps(Props.Size() + (dataSizeReduce ? 1 : 0));
185   FOR_VECTOR (i, Props)
186     coderProps.AddProp(Props[i]);
187   if (dataSizeReduce)
188   {
189     CProp prop;
190     prop.Id = NCoderPropID::kReduceSize;
191     prop.Value = *dataSizeReduce;
192     coderProps.AddProp(prop);
193   }
194   return coderProps.SetProps(scp);
195 }
196 
197 
FindProp(PROPID id) const198 int CMethodProps::FindProp(PROPID id) const
199 {
200   for (int i = Props.Size() - 1; i >= 0; i--)
201     if (Props[i].Id == id)
202       return i;
203   return -1;
204 }
205 
GetLevel() const206 int CMethodProps::GetLevel() const
207 {
208   int i = FindProp(NCoderPropID::kLevel);
209   if (i < 0)
210     return 5;
211   if (Props[i].Value.vt != VT_UI4)
212     return 9;
213   UInt32 level = Props[i].Value.ulVal;
214   return level > 9 ? 9 : (int)level;
215 }
216 
217 struct CNameToPropID
218 {
219   VARTYPE VarType;
220   const char *Name;
221 };
222 
223 static const CNameToPropID g_NameToPropID[] =
224 {
225   { VT_UI4, "" },
226   { VT_UI4, "d" },
227   { VT_UI4, "mem" },
228   { VT_UI4, "o" },
229   { VT_UI4, "c" },
230   { VT_UI4, "pb" },
231   { VT_UI4, "lc" },
232   { VT_UI4, "lp" },
233   { VT_UI4, "fb" },
234   { VT_BSTR, "mf" },
235   { VT_UI4, "mc" },
236   { VT_UI4, "pass" },
237   { VT_UI4, "a" },
238   { VT_UI4, "mt" },
239   { VT_BOOL, "eos" },
240   { VT_UI4, "x" },
241   { VT_UI4, "reduceSize" }
242 };
243 
FindPropIdExact(const UString & name)244 static int FindPropIdExact(const UString &name)
245 {
246   for (unsigned i = 0; i < ARRAY_SIZE(g_NameToPropID); i++)
247     if (StringsAreEqualNoCase_Ascii(name, g_NameToPropID[i].Name))
248       return i;
249   return -1;
250 }
251 
ConvertProperty(const PROPVARIANT & srcProp,VARTYPE varType,NCOM::CPropVariant & destProp)252 static bool ConvertProperty(const PROPVARIANT &srcProp, VARTYPE varType, NCOM::CPropVariant &destProp)
253 {
254   if (varType == srcProp.vt)
255   {
256     destProp = srcProp;
257     return true;
258   }
259   if (varType == VT_BOOL)
260   {
261     bool res;
262     if (PROPVARIANT_to_bool(srcProp, res) != S_OK)
263       return false;
264     destProp = res;
265     return true;
266   }
267   if (srcProp.vt == VT_EMPTY)
268   {
269     destProp = srcProp;
270     return true;
271   }
272   return false;
273 }
274 
SplitParams(const UString & srcString,UStringVector & subStrings)275 static void SplitParams(const UString &srcString, UStringVector &subStrings)
276 {
277   subStrings.Clear();
278   UString s;
279   int len = srcString.Len();
280   if (len == 0)
281     return;
282   for (int i = 0; i < len; i++)
283   {
284     wchar_t c = srcString[i];
285     if (c == L':')
286     {
287       subStrings.Add(s);
288       s.Empty();
289     }
290     else
291       s += c;
292   }
293   subStrings.Add(s);
294 }
295 
SplitParam(const UString & param,UString & name,UString & value)296 static void SplitParam(const UString &param, UString &name, UString &value)
297 {
298   int eqPos = param.Find(L'=');
299   if (eqPos >= 0)
300   {
301     name.SetFrom(param, eqPos);
302     value = param.Ptr(eqPos + 1);
303     return;
304   }
305   unsigned i;
306   for (i = 0; i < param.Len(); i++)
307   {
308     wchar_t c = param[i];
309     if (c >= L'0' && c <= L'9')
310       break;
311   }
312   name.SetFrom(param, i);
313   value = param.Ptr(i);
314 }
315 
IsLogSizeProp(PROPID propid)316 static bool IsLogSizeProp(PROPID propid)
317 {
318   switch (propid)
319   {
320     case NCoderPropID::kDictionarySize:
321     case NCoderPropID::kUsedMemorySize:
322     case NCoderPropID::kBlockSize:
323     case NCoderPropID::kReduceSize:
324       return true;
325   }
326   return false;
327 }
328 
SetParam(const UString & name,const UString & value)329 HRESULT CMethodProps::SetParam(const UString &name, const UString &value)
330 {
331   int index = FindPropIdExact(name);
332   if (index < 0)
333     return E_INVALIDARG;
334   const CNameToPropID &nameToPropID = g_NameToPropID[index];
335   CProp prop;
336   prop.Id = index;
337 
338   if (IsLogSizeProp(prop.Id))
339   {
340     UInt32 dicSize;
341     RINOK(StringToDictSize(value, dicSize));
342     prop.Value = dicSize;
343   }
344   else
345   {
346     NCOM::CPropVariant propValue;
347     if (nameToPropID.VarType == VT_BSTR)
348       propValue = value;
349     else if (nameToPropID.VarType == VT_BOOL)
350     {
351       bool res;
352       if (!StringToBool(value, res))
353         return E_INVALIDARG;
354       propValue = res;
355     }
356     else if (!value.IsEmpty())
357     {
358       UInt32 number;
359       if (ParseStringToUInt32(value, number) == value.Len())
360         propValue = number;
361       else
362         propValue = value;
363     }
364     if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value))
365       return E_INVALIDARG;
366   }
367   Props.Add(prop);
368   return S_OK;
369 }
370 
ParseParamsFromString(const UString & srcString)371 HRESULT CMethodProps::ParseParamsFromString(const UString &srcString)
372 {
373   UStringVector params;
374   SplitParams(srcString, params);
375   FOR_VECTOR (i, params)
376   {
377     const UString &param = params[i];
378     UString name, value;
379     SplitParam(param, name, value);
380     RINOK(SetParam(name, value));
381   }
382   return S_OK;
383 }
384 
ParseParamsFromPROPVARIANT(const UString & realName,const PROPVARIANT & value)385 HRESULT CMethodProps::ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value)
386 {
387   if (realName.Len() == 0)
388   {
389     // [empty]=method
390     return E_INVALIDARG;
391   }
392   if (value.vt == VT_EMPTY)
393   {
394     // {realName}=[empty]
395     UString name, value;
396     SplitParam(realName, name, value);
397     return SetParam(name, value);
398   }
399 
400   // {realName}=value
401   int index = FindPropIdExact(realName);
402   if (index < 0)
403     return E_INVALIDARG;
404   const CNameToPropID &nameToPropID = g_NameToPropID[index];
405   CProp prop;
406   prop.Id = index;
407 
408   if (IsLogSizeProp(prop.Id))
409   {
410     UInt32 dicSize;
411     RINOK(PROPVARIANT_to_DictSize(value, dicSize));
412     prop.Value = dicSize;
413   }
414   else
415   {
416     if (!ConvertProperty(value, nameToPropID.VarType, prop.Value))
417       return E_INVALIDARG;
418   }
419   Props.Add(prop);
420   return S_OK;
421 }
422 
ParseMethodFromString(const UString & s)423 HRESULT COneMethodInfo::ParseMethodFromString(const UString &s)
424 {
425   int splitPos = s.Find(':');
426   MethodName = s;
427   if (splitPos < 0)
428     return S_OK;
429   MethodName.DeleteFrom(splitPos);
430   return ParseParamsFromString(s.Ptr(splitPos + 1));
431 }
432 
ParseMethodFromPROPVARIANT(const UString & realName,const PROPVARIANT & value)433 HRESULT COneMethodInfo::ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value)
434 {
435   if (!realName.IsEmpty() && !StringsAreEqualNoCase_Ascii(realName, "m"))
436     return ParseParamsFromPROPVARIANT(realName, value);
437   // -m{N}=method
438   if (value.vt != VT_BSTR)
439     return E_INVALIDARG;
440   return ParseMethodFromString(value.bstrVal);
441 }
442