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[0] == '+' && s[1] == 0) || StringsAreEqualNoCase_Ascii(s, "ON"))
14   {
15     res = true;
16     return true;
17   }
18   if ((s[0] == '-' && s[1] == 0) || 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 
93 
StringToDictSize(const UString & s,NCOM::CPropVariant & destProp)94 static HRESULT StringToDictSize(const UString &s, NCOM::CPropVariant &destProp)
95 {
96   const wchar_t *end;
97   UInt32 number = ConvertStringToUInt32(s, &end);
98   unsigned numDigits = (unsigned)(end - s);
99   if (numDigits == 0 || s.Len() > numDigits + 1)
100     return E_INVALIDARG;
101 
102   if (s.Len() == numDigits)
103   {
104     if (number >= 64)
105       return E_INVALIDARG;
106     if (number < 32)
107       destProp = (UInt32)((UInt32)1 << (unsigned)number);
108     else
109       destProp = (UInt64)((UInt64)1 << (unsigned)number);
110     return S_OK;
111   }
112 
113   unsigned numBits;
114 
115   switch (MyCharLower_Ascii(s[numDigits]))
116   {
117     case 'b': destProp = number; return S_OK;
118     case 'k': numBits = 10; break;
119     case 'm': numBits = 20; break;
120     case 'g': numBits = 30; break;
121     default: return E_INVALIDARG;
122   }
123 
124   if (number < ((UInt32)1 << (32 - numBits)))
125     destProp = (UInt32)(number << numBits);
126   else
127     destProp = (UInt64)((UInt64)number << numBits);
128 
129   return S_OK;
130 }
131 
132 
PROPVARIANT_to_DictSize(const PROPVARIANT & prop,NCOM::CPropVariant & destProp)133 static HRESULT PROPVARIANT_to_DictSize(const PROPVARIANT &prop, NCOM::CPropVariant &destProp)
134 {
135   if (prop.vt == VT_UI4)
136   {
137     UInt32 v = prop.ulVal;
138     if (v >= 64)
139       return E_INVALIDARG;
140     if (v < 32)
141       destProp = (UInt32)((UInt32)1 << (unsigned)v);
142     else
143       destProp = (UInt64)((UInt64)1 << (unsigned)v);
144     return S_OK;
145   }
146   if (prop.vt == VT_BSTR)
147     return StringToDictSize(prop.bstrVal, destProp);
148   return E_INVALIDARG;
149 }
150 
151 
AddProp32(PROPID propid,UInt32 level)152 void CProps::AddProp32(PROPID propid, UInt32 level)
153 {
154   CProp &prop = Props.AddNew();
155   prop.IsOptional = true;
156   prop.Id = propid;
157   prop.Value = (UInt32)level;
158 }
159 
160 class CCoderProps
161 {
162   PROPID *_propIDs;
163   NCOM::CPropVariant *_props;
164   unsigned _numProps;
165   unsigned _numPropsMax;
166 public:
CCoderProps(unsigned numPropsMax)167   CCoderProps(unsigned numPropsMax)
168   {
169     _numPropsMax = numPropsMax;
170     _numProps = 0;
171     _propIDs = new PROPID[numPropsMax];
172     _props = new NCOM::CPropVariant[numPropsMax];
173   }
~CCoderProps()174   ~CCoderProps()
175   {
176     delete []_propIDs;
177     delete []_props;
178   }
179   void AddProp(const CProp &prop);
SetProps(ICompressSetCoderProperties * setCoderProperties)180   HRESULT SetProps(ICompressSetCoderProperties *setCoderProperties)
181   {
182     return setCoderProperties->SetCoderProperties(_propIDs, _props, _numProps);
183   }
184 };
185 
AddProp(const CProp & prop)186 void CCoderProps::AddProp(const CProp &prop)
187 {
188   if (_numProps >= _numPropsMax)
189     throw 1;
190   _propIDs[_numProps] = prop.Id;
191   _props[_numProps] = prop.Value;
192   _numProps++;
193 }
194 
SetCoderProps(ICompressSetCoderProperties * scp,const UInt64 * dataSizeReduce) const195 HRESULT CProps::SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const
196 {
197   CCoderProps coderProps(Props.Size() + (dataSizeReduce ? 1 : 0));
198   FOR_VECTOR (i, Props)
199     coderProps.AddProp(Props[i]);
200   if (dataSizeReduce)
201   {
202     CProp prop;
203     prop.Id = NCoderPropID::kReduceSize;
204     prop.Value = *dataSizeReduce;
205     coderProps.AddProp(prop);
206   }
207   return coderProps.SetProps(scp);
208 }
209 
210 
FindProp(PROPID id) const211 int CMethodProps::FindProp(PROPID id) const
212 {
213   for (int i = Props.Size() - 1; i >= 0; i--)
214     if (Props[i].Id == id)
215       return i;
216   return -1;
217 }
218 
GetLevel() const219 int CMethodProps::GetLevel() const
220 {
221   int i = FindProp(NCoderPropID::kLevel);
222   if (i < 0)
223     return 5;
224   if (Props[i].Value.vt != VT_UI4)
225     return 9;
226   UInt32 level = Props[i].Value.ulVal;
227   return level > 9 ? 9 : (int)level;
228 }
229 
230 struct CNameToPropID
231 {
232   VARTYPE VarType;
233   const char *Name;
234 };
235 
236 static const CNameToPropID g_NameToPropID[] =
237 {
238   { VT_UI4, "" },
239   { VT_UI4, "d" },
240   { VT_UI4, "mem" },
241   { VT_UI4, "o" },
242   { VT_UI4, "c" },
243   { VT_UI4, "pb" },
244   { VT_UI4, "lc" },
245   { VT_UI4, "lp" },
246   { VT_UI4, "fb" },
247   { VT_BSTR, "mf" },
248   { VT_UI4, "mc" },
249   { VT_UI4, "pass" },
250   { VT_UI4, "a" },
251   { VT_UI4, "mt" },
252   { VT_BOOL, "eos" },
253   { VT_UI4, "x" },
254   { VT_UI4, "reduceSize" }
255 };
256 
FindPropIdExact(const UString & name)257 static int FindPropIdExact(const UString &name)
258 {
259   for (unsigned i = 0; i < ARRAY_SIZE(g_NameToPropID); i++)
260     if (StringsAreEqualNoCase_Ascii(name, g_NameToPropID[i].Name))
261       return i;
262   return -1;
263 }
264 
ConvertProperty(const PROPVARIANT & srcProp,VARTYPE varType,NCOM::CPropVariant & destProp)265 static bool ConvertProperty(const PROPVARIANT &srcProp, VARTYPE varType, NCOM::CPropVariant &destProp)
266 {
267   if (varType == srcProp.vt)
268   {
269     destProp = srcProp;
270     return true;
271   }
272   if (varType == VT_BOOL)
273   {
274     bool res;
275     if (PROPVARIANT_to_bool(srcProp, res) != S_OK)
276       return false;
277     destProp = res;
278     return true;
279   }
280   if (srcProp.vt == VT_EMPTY)
281   {
282     destProp = srcProp;
283     return true;
284   }
285   return false;
286 }
287 
SplitParams(const UString & srcString,UStringVector & subStrings)288 static void SplitParams(const UString &srcString, UStringVector &subStrings)
289 {
290   subStrings.Clear();
291   UString s;
292   unsigned len = srcString.Len();
293   if (len == 0)
294     return;
295   for (unsigned i = 0; i < len; i++)
296   {
297     wchar_t c = srcString[i];
298     if (c == L':')
299     {
300       subStrings.Add(s);
301       s.Empty();
302     }
303     else
304       s += c;
305   }
306   subStrings.Add(s);
307 }
308 
SplitParam(const UString & param,UString & name,UString & value)309 static void SplitParam(const UString &param, UString &name, UString &value)
310 {
311   int eqPos = param.Find(L'=');
312   if (eqPos >= 0)
313   {
314     name.SetFrom(param, eqPos);
315     value = param.Ptr(eqPos + 1);
316     return;
317   }
318   unsigned i;
319   for (i = 0; i < param.Len(); i++)
320   {
321     wchar_t c = param[i];
322     if (c >= L'0' && c <= L'9')
323       break;
324   }
325   name.SetFrom(param, i);
326   value = param.Ptr(i);
327 }
328 
IsLogSizeProp(PROPID propid)329 static bool IsLogSizeProp(PROPID propid)
330 {
331   switch (propid)
332   {
333     case NCoderPropID::kDictionarySize:
334     case NCoderPropID::kUsedMemorySize:
335     case NCoderPropID::kBlockSize:
336     case NCoderPropID::kReduceSize:
337       return true;
338   }
339   return false;
340 }
341 
SetParam(const UString & name,const UString & value)342 HRESULT CMethodProps::SetParam(const UString &name, const UString &value)
343 {
344   int index = FindPropIdExact(name);
345   if (index < 0)
346     return E_INVALIDARG;
347   const CNameToPropID &nameToPropID = g_NameToPropID[(unsigned)index];
348   CProp prop;
349   prop.Id = index;
350 
351   if (IsLogSizeProp(prop.Id))
352   {
353     RINOK(StringToDictSize(value, prop.Value));
354   }
355   else
356   {
357     NCOM::CPropVariant propValue;
358     if (nameToPropID.VarType == VT_BSTR)
359       propValue = value;
360     else if (nameToPropID.VarType == VT_BOOL)
361     {
362       bool res;
363       if (!StringToBool(value, res))
364         return E_INVALIDARG;
365       propValue = res;
366     }
367     else if (!value.IsEmpty())
368     {
369       UInt32 number;
370       if (ParseStringToUInt32(value, number) == value.Len())
371         propValue = number;
372       else
373         propValue = value;
374     }
375     if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value))
376       return E_INVALIDARG;
377   }
378   Props.Add(prop);
379   return S_OK;
380 }
381 
ParseParamsFromString(const UString & srcString)382 HRESULT CMethodProps::ParseParamsFromString(const UString &srcString)
383 {
384   UStringVector params;
385   SplitParams(srcString, params);
386   FOR_VECTOR (i, params)
387   {
388     const UString &param = params[i];
389     UString name, value;
390     SplitParam(param, name, value);
391     RINOK(SetParam(name, value));
392   }
393   return S_OK;
394 }
395 
ParseParamsFromPROPVARIANT(const UString & realName,const PROPVARIANT & value)396 HRESULT CMethodProps::ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value)
397 {
398   if (realName.Len() == 0)
399   {
400     // [empty]=method
401     return E_INVALIDARG;
402   }
403   if (value.vt == VT_EMPTY)
404   {
405     // {realName}=[empty]
406     UString name, valueStr;
407     SplitParam(realName, name, valueStr);
408     return SetParam(name, valueStr);
409   }
410 
411   // {realName}=value
412   int index = FindPropIdExact(realName);
413   if (index < 0)
414     return E_INVALIDARG;
415   const CNameToPropID &nameToPropID = g_NameToPropID[(unsigned)index];
416   CProp prop;
417   prop.Id = index;
418 
419   if (IsLogSizeProp(prop.Id))
420   {
421     RINOK(PROPVARIANT_to_DictSize(value, prop.Value));
422   }
423   else
424   {
425     if (!ConvertProperty(value, nameToPropID.VarType, prop.Value))
426       return E_INVALIDARG;
427   }
428   Props.Add(prop);
429   return S_OK;
430 }
431 
ParseMethodFromString(const UString & s)432 HRESULT COneMethodInfo::ParseMethodFromString(const UString &s)
433 {
434   MethodName.Empty();
435   int splitPos = s.Find(L':');
436   {
437     UString temp = s;
438     if (splitPos >= 0)
439       temp.DeleteFrom(splitPos);
440     if (!temp.IsAscii())
441       return E_INVALIDARG;
442     MethodName.SetFromWStr_if_Ascii(temp);
443   }
444   if (splitPos < 0)
445     return S_OK;
446   PropsString = s.Ptr(splitPos + 1);
447   return ParseParamsFromString(PropsString);
448 }
449 
ParseMethodFromPROPVARIANT(const UString & realName,const PROPVARIANT & value)450 HRESULT COneMethodInfo::ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value)
451 {
452   if (!realName.IsEmpty() && !StringsAreEqualNoCase_Ascii(realName, "m"))
453     return ParseParamsFromPROPVARIANT(realName, value);
454   // -m{N}=method
455   if (value.vt != VT_BSTR)
456     return E_INVALIDARG;
457   return ParseMethodFromString(value.bstrVal);
458 }
459