1// MethodProps.cpp
2
3#include "StdAfx.h"
4
5#include "../../Common/StringToInt.h"
6
7#include "MethodProps.h"
8
9using namespace NWindows;
10
11UInt64 Calc_From_Val_Percents(UInt64 val, UInt64 percents)
12{
13  // if (percents == 0) return 0;
14  const UInt64 q = percents / 100;
15  const UInt32 r = (UInt32)(percents % 100);
16  UInt64 res = 0;
17
18  if (q != 0)
19  {
20    if (val > (UInt64)(Int64)-1 / q)
21      return (UInt64)(Int64)-1;
22    res = val * q;
23  }
24
25  if (r != 0)
26  {
27    UInt64 v2;
28    if (val <= (UInt64)(Int64)-1 / r)
29      v2 = val * r / 100;
30    else
31      v2 = val / 100 * r;
32    res += v2;
33    if (res < v2)
34      return (UInt64)(Int64)-1;
35  }
36
37  return res;
38}
39
40
41bool StringToBool(const wchar_t *s, bool &res)
42{
43  if (s[0] == 0 || (s[0] == '+' && s[1] == 0) || StringsAreEqualNoCase_Ascii(s, "ON"))
44  {
45    res = true;
46    return true;
47  }
48  if ((s[0] == '-' && s[1] == 0) || StringsAreEqualNoCase_Ascii(s, "OFF"))
49  {
50    res = false;
51    return true;
52  }
53  return false;
54}
55
56HRESULT PROPVARIANT_to_bool(const PROPVARIANT &prop, bool &dest)
57{
58  switch (prop.vt)
59  {
60    case VT_EMPTY: dest = true; return S_OK;
61    case VT_BOOL: dest = (prop.boolVal != VARIANT_FALSE); return S_OK;
62    case VT_BSTR: return StringToBool(prop.bstrVal, dest) ? S_OK : E_INVALIDARG;
63  }
64  return E_INVALIDARG;
65}
66
67unsigned ParseStringToUInt32(const UString &srcString, UInt32 &number)
68{
69  const wchar_t *start = srcString;
70  const wchar_t *end;
71  number = ConvertStringToUInt32(start, &end);
72  return (unsigned)(end - start);
73}
74
75static unsigned ParseStringToUInt64(const UString &srcString, UInt64 &number)
76{
77  const wchar_t *start = srcString;
78  const wchar_t *end;
79  number = ConvertStringToUInt64(start, &end);
80  return (unsigned)(end - start);
81}
82
83HRESULT ParsePropToUInt32(const UString &name, const PROPVARIANT &prop, UInt32 &resValue)
84{
85  // =VT_UI4
86  // =VT_EMPTY : it doesn't change (resValue), and returns S_OK
87  // {stringUInt32}=VT_EMPTY
88
89  if (prop.vt == VT_UI4)
90  {
91    if (!name.IsEmpty())
92      return E_INVALIDARG;
93    resValue = prop.ulVal;
94    return S_OK;
95  }
96  if (prop.vt != VT_EMPTY)
97    return E_INVALIDARG;
98  if (name.IsEmpty())
99    return S_OK;
100  UInt32 v;
101  if (ParseStringToUInt32(name, v) != name.Len())
102    return E_INVALIDARG;
103  resValue = v;
104  return S_OK;
105}
106
107
108
109HRESULT ParseMtProp2(const UString &name, const PROPVARIANT &prop, UInt32 &numThreads, bool &force)
110{
111  force = false;
112  UString s;
113  if (name.IsEmpty())
114  {
115    if (prop.vt == VT_UI4)
116    {
117      numThreads = prop.ulVal;
118      force = true;
119      return S_OK;
120    }
121    bool val;
122    HRESULT res = PROPVARIANT_to_bool(prop, val);
123    if (res == S_OK)
124    {
125      if (!val)
126      {
127        numThreads = 1;
128        force = true;
129      }
130      // force = true; for debug
131      // "(VT_BOOL = VARIANT_TRUE)" set "force = false" and doesn't change numThreads
132      return S_OK;
133    }
134    if (prop.vt != VT_BSTR)
135      return res;
136    s.SetFromBstr(prop.bstrVal);
137    if (s.IsEmpty())
138      return E_INVALIDARG;
139  }
140  else
141  {
142    if (prop.vt != VT_EMPTY)
143      return E_INVALIDARG;
144    s = name;
145  }
146
147  s.MakeLower_Ascii();
148  const wchar_t *start = s;
149  UInt32 v = numThreads;
150
151  /* we force up, if threads number specified
152     only `d` will force it down */
153  bool force_loc = true;
154  for (;;)
155  {
156    const wchar_t c = *start;
157    if (!c)
158      break;
159    if (c == 'd')
160    {
161      force_loc = false;  // force down
162      start++;
163      continue;
164    }
165    if (c == 'u')
166    {
167      force_loc = true;   // force up
168      start++;
169      continue;
170    }
171    bool isPercent = false;
172    if (c == 'p')
173    {
174      isPercent = true;
175      start++;
176    }
177    const wchar_t *end;
178    v = ConvertStringToUInt32(start, &end);
179    if (end == start)
180      return E_INVALIDARG;
181    if (isPercent)
182      v = numThreads * v / 100;
183    start = end;
184  }
185
186  numThreads = v;
187  force = force_loc;
188  return S_OK;
189}
190
191
192
193static HRESULT SetLogSizeProp(UInt64 number, NCOM::CPropVariant &destProp)
194{
195  if (number >= 64)
196    return E_INVALIDARG;
197  UInt32 val32;
198  if (number < 32)
199    val32 = (UInt32)1 << (unsigned)number;
200  /*
201  else if (number == 32 && reduce_4GB_to_32bits)
202    val32 = (UInt32)(Int32)-1;
203  */
204  else
205  {
206    destProp = (UInt64)((UInt64)1 << (unsigned)number);
207    return S_OK;
208  }
209  destProp = (UInt32)val32;
210  return S_OK;
211}
212
213
214static HRESULT StringToDictSize(const UString &s, NCOM::CPropVariant &destProp)
215{
216  /* if (reduce_4GB_to_32bits) we can reduce (4 GiB) property to (4 GiB - 1).
217     to fit the value to UInt32 for clients that do not support 64-bit values */
218
219  const wchar_t *end;
220  const UInt64 number = ConvertStringToUInt64(s, &end);
221  const unsigned numDigits = (unsigned)(end - s.Ptr());
222  if (numDigits == 0 || s.Len() > numDigits + 1)
223    return E_INVALIDARG;
224
225  if (s.Len() == numDigits)
226    return SetLogSizeProp(number, destProp);
227
228  unsigned numBits;
229
230  switch (MyCharLower_Ascii(s[numDigits]))
231  {
232    case 'b': numBits =  0; break;
233    case 'k': numBits = 10; break;
234    case 'm': numBits = 20; break;
235    case 'g': numBits = 30; break;
236    default: return E_INVALIDARG;
237  }
238
239  const UInt64 range4g = ((UInt64)1 << (32 - numBits));
240  if (number < range4g)
241    destProp = (UInt32)((UInt32)number << numBits);
242  /*
243  else if (number == range4g && reduce_4GB_to_32bits)
244    destProp = (UInt32)(Int32)-1;
245  */
246  else if (numBits == 0)
247    destProp = (UInt64)number;
248  else if (number >= ((UInt64)1 << (64 - numBits)))
249    return E_INVALIDARG;
250  else
251    destProp = (UInt64)((UInt64)number << numBits);
252  return S_OK;
253}
254
255
256static HRESULT PROPVARIANT_to_DictSize(const PROPVARIANT &prop, NCOM::CPropVariant &destProp)
257{
258  if (prop.vt == VT_UI4)
259    return SetLogSizeProp(prop.ulVal, destProp);
260
261  if (prop.vt == VT_BSTR)
262  {
263    UString s;
264    s = prop.bstrVal;
265    return StringToDictSize(s, destProp);
266  }
267  return E_INVALIDARG;
268}
269
270
271void CProps::AddProp32(PROPID propid, UInt32 val)
272{
273  CProp &prop = Props.AddNew();
274  prop.IsOptional = true;
275  prop.Id = propid;
276  prop.Value = (UInt32)val;
277}
278
279void CProps::AddPropBool(PROPID propid, bool val)
280{
281  CProp &prop = Props.AddNew();
282  prop.IsOptional = true;
283  prop.Id = propid;
284  prop.Value = val;
285}
286
287class CCoderProps
288{
289  PROPID *_propIDs;
290  NCOM::CPropVariant *_props;
291  unsigned _numProps;
292  unsigned _numPropsMax;
293public:
294  CCoderProps(unsigned numPropsMax):
295      _propIDs(NULL),
296      _props(NULL),
297      _numProps(0),
298      _numPropsMax(numPropsMax)
299  {
300    _propIDs = new PROPID[numPropsMax];
301    _props = new NCOM::CPropVariant[numPropsMax];
302  }
303  ~CCoderProps()
304  {
305    delete []_propIDs;
306    delete []_props;
307  }
308  void AddProp(const CProp &prop);
309  HRESULT SetProps(ICompressSetCoderProperties *setCoderProperties)
310  {
311    return setCoderProperties->SetCoderProperties(_propIDs, _props, _numProps);
312  }
313};
314
315void CCoderProps::AddProp(const CProp &prop)
316{
317  if (_numProps >= _numPropsMax)
318    throw 1;
319  _propIDs[_numProps] = prop.Id;
320  _props[_numProps] = prop.Value;
321  _numProps++;
322}
323
324HRESULT CProps::SetCoderProps(ICompressSetCoderProperties *scp, const UInt64 *dataSizeReduce) const
325{
326  return SetCoderProps_DSReduce_Aff(scp, dataSizeReduce, NULL);
327}
328
329HRESULT CProps::SetCoderProps_DSReduce_Aff(
330    ICompressSetCoderProperties *scp,
331    const UInt64 *dataSizeReduce,
332    const UInt64 *affinity) const
333{
334  CCoderProps coderProps(Props.Size() + (dataSizeReduce ? 1 : 0) + (affinity ? 1 : 0) );
335  FOR_VECTOR (i, Props)
336    coderProps.AddProp(Props[i]);
337  if (dataSizeReduce)
338  {
339    CProp prop;
340    prop.Id = NCoderPropID::kReduceSize;
341    prop.Value = *dataSizeReduce;
342    coderProps.AddProp(prop);
343  }
344  if (affinity)
345  {
346    CProp prop;
347    prop.Id = NCoderPropID::kAffinity;
348    prop.Value = *affinity;
349    coderProps.AddProp(prop);
350  }
351  return coderProps.SetProps(scp);
352}
353
354
355int CMethodProps::FindProp(PROPID id) const
356{
357  for (unsigned i = Props.Size(); i != 0;)
358    if (Props[--i].Id == id)
359      return (int)i;
360  return -1;
361}
362
363unsigned CMethodProps::GetLevel() const
364{
365  int i = FindProp(NCoderPropID::kLevel);
366  if (i < 0)
367    return 5;
368  if (Props[(unsigned)i].Value.vt != VT_UI4)
369    return 9;
370  UInt32 level = Props[(unsigned)i].Value.ulVal;
371  return level > 9 ? 9 : (unsigned)level;
372}
373
374struct CNameToPropID
375{
376  VARTYPE VarType;
377  const char *Name;
378};
379
380
381// the following are related to NCoderPropID::EEnum values
382// NCoderPropID::k_NUM_DEFINED
383static const CNameToPropID g_NameToPropID[] =
384{
385  { VT_UI4, "" },
386  { VT_UI4, "d" },
387  { VT_UI4, "mem" },
388  { VT_UI4, "o" },
389  { VT_UI8, "c" },
390  { VT_UI4, "pb" },
391  { VT_UI4, "lc" },
392  { VT_UI4, "lp" },
393  { VT_UI4, "fb" },
394  { VT_BSTR, "mf" },
395  { VT_UI4, "mc" },
396  { VT_UI4, "pass" },
397  { VT_UI4, "a" },
398  { VT_UI4, "mt" },
399  { VT_BOOL, "eos" },
400  { VT_UI4, "x" },
401  { VT_UI8, "reduce" },
402  { VT_UI8, "expect" },
403  { VT_UI8, "cc" }, // "cc" in v23,  "b" in v22.01
404  { VT_UI4, "check" },
405  { VT_BSTR, "filter" },
406  { VT_UI8, "memuse" },
407  { VT_UI8, "aff" },
408  { VT_UI4, "offset" },
409  { VT_UI4, "zhb" }
410  /*
411  ,
412  // { VT_UI4, "zhc" },
413  // { VT_UI4, "zhd" },
414  // { VT_UI4, "zcb" },
415  { VT_UI4, "dc" },
416  { VT_UI4, "zx" },
417  { VT_UI4, "zf" },
418  { VT_UI4, "zmml" },
419  { VT_UI4, "zov" },
420  { VT_BOOL, "zmfr" },
421  { VT_BOOL, "zle" }, // long enable
422  // { VT_UI4, "zldb" },
423  { VT_UI4, "zld" },
424  { VT_UI4, "zlhb" },
425  { VT_UI4, "zlmml" },
426  { VT_UI4, "zlbb" },
427  { VT_UI4, "zlhrb" },
428  { VT_BOOL, "zwus" },
429  { VT_BOOL, "zshp" },
430  { VT_BOOL, "zshs" },
431  { VT_BOOL, "zshe" },
432  { VT_BOOL, "zshg" },
433  { VT_UI4, "zpsm" }
434  */
435  // { VT_UI4, "mcb" }, // mc log version
436  // { VT_UI4, "ztlen" },  // fb ?
437};
438
439/*
440#if defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 200410L) || (defined(_MSC_VER) && _MSC_VER >= 1600)
441
442#if (defined(__cplusplus) && __cplusplus < 201103L) \
443    && defined(__clang__) && __clang_major__ >= 4
444#pragma GCC diagnostic ignored "-Wc11-extensions"
445#endif
446  static_assert(Z7_ARRAY_SIZE(g_NameToPropID) == NCoderPropID::k_NUM_DEFINED,
447    "g_NameToPropID doesn't match NCoderPropID enum");
448#endif
449*/
450
451static int FindPropIdExact(const UString &name)
452{
453  for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_NameToPropID); i++)
454    if (StringsAreEqualNoCase_Ascii(name, g_NameToPropID[i].Name))
455      return (int)i;
456  return -1;
457}
458
459static bool ConvertProperty(const PROPVARIANT &srcProp, VARTYPE varType, NCOM::CPropVariant &destProp)
460{
461  if (varType == srcProp.vt)
462  {
463    destProp = srcProp;
464    return true;
465  }
466
467  if (varType == VT_UI8 && srcProp.vt == VT_UI4)
468  {
469    destProp = (UInt64)srcProp.ulVal;
470    return true;
471  }
472
473  if (varType == VT_BOOL)
474  {
475    bool res;
476    if (PROPVARIANT_to_bool(srcProp, res) != S_OK)
477      return false;
478    destProp = res;
479    return true;
480  }
481  if (srcProp.vt == VT_EMPTY)
482  {
483    destProp = srcProp;
484    return true;
485  }
486  return false;
487}
488
489static void SplitParams(const UString &srcString, UStringVector &subStrings)
490{
491  subStrings.Clear();
492  UString s;
493  unsigned len = srcString.Len();
494  if (len == 0)
495    return;
496  for (unsigned i = 0; i < len; i++)
497  {
498    wchar_t c = srcString[i];
499    if (c == L':')
500    {
501      subStrings.Add(s);
502      s.Empty();
503    }
504    else
505      s += c;
506  }
507  subStrings.Add(s);
508}
509
510static void SplitParam(const UString &param, UString &name, UString &value)
511{
512  int eqPos = param.Find(L'=');
513  if (eqPos >= 0)
514  {
515    name.SetFrom(param, (unsigned)eqPos);
516    value = param.Ptr((unsigned)(eqPos + 1));
517    return;
518  }
519  unsigned i;
520  for (i = 0; i < param.Len(); i++)
521  {
522    wchar_t c = param[i];
523    if (c >= L'0' && c <= L'9')
524      break;
525  }
526  name.SetFrom(param, i);
527  value = param.Ptr(i);
528}
529
530static bool IsLogSizeProp(PROPID propid)
531{
532  switch (propid)
533  {
534    case NCoderPropID::kDictionarySize:
535    case NCoderPropID::kUsedMemorySize:
536    case NCoderPropID::kBlockSize:
537    case NCoderPropID::kBlockSize2:
538    /*
539    case NCoderPropID::kChainSize:
540    case NCoderPropID::kLdmWindowSize:
541    */
542    // case NCoderPropID::kReduceSize:
543      return true;
544  }
545  return false;
546}
547
548HRESULT CMethodProps::SetParam(const UString &name, const UString &value)
549{
550  int index = FindPropIdExact(name);
551  if (index < 0)
552  {
553    // 'b' was used as NCoderPropID::kBlockSize2 before v23
554    if (!name.IsEqualTo_Ascii_NoCase("b") || value.Find(L':') >= 0)
555      return E_INVALIDARG;
556    index = NCoderPropID::kBlockSize2;
557  }
558  const CNameToPropID &nameToPropID = g_NameToPropID[(unsigned)index];
559  CProp prop;
560  prop.Id = (unsigned)index;
561
562  if (IsLogSizeProp(prop.Id))
563  {
564    RINOK(StringToDictSize(value, prop.Value))
565  }
566  else
567  {
568    NCOM::CPropVariant propValue;
569    if (nameToPropID.VarType == VT_BSTR)
570      propValue = value;
571    else if (nameToPropID.VarType == VT_BOOL)
572    {
573      bool res;
574      if (!StringToBool(value, res))
575        return E_INVALIDARG;
576      propValue = res;
577    }
578    else if (!value.IsEmpty())
579    {
580      if (nameToPropID.VarType == VT_UI4)
581      {
582        UInt32 number;
583        if (ParseStringToUInt32(value, number) == value.Len())
584          propValue = number;
585        else
586          propValue = value;
587      }
588      else if (nameToPropID.VarType == VT_UI8)
589      {
590        UInt64 number;
591        if (ParseStringToUInt64(value, number) == value.Len())
592          propValue = number;
593        else
594          propValue = value;
595      }
596      else
597        propValue = value;
598    }
599    if (!ConvertProperty(propValue, nameToPropID.VarType, prop.Value))
600      return E_INVALIDARG;
601  }
602  Props.Add(prop);
603  return S_OK;
604}
605
606HRESULT CMethodProps::ParseParamsFromString(const UString &srcString)
607{
608  UStringVector params;
609  SplitParams(srcString, params);
610  FOR_VECTOR (i, params)
611  {
612    const UString &param = params[i];
613    UString name, value;
614    SplitParam(param, name, value);
615    RINOK(SetParam(name, value))
616  }
617  return S_OK;
618}
619
620HRESULT CMethodProps::ParseParamsFromPROPVARIANT(const UString &realName, const PROPVARIANT &value)
621{
622  if (realName.Len() == 0)
623  {
624    // [empty]=method
625    return E_INVALIDARG;
626  }
627  if (value.vt == VT_EMPTY)
628  {
629    // {realName}=[empty]
630    UString name, valueStr;
631    SplitParam(realName, name, valueStr);
632    return SetParam(name, valueStr);
633  }
634
635  // {realName}=value
636  const int index = FindPropIdExact(realName);
637  if (index < 0)
638    return E_INVALIDARG;
639  const CNameToPropID &nameToPropID = g_NameToPropID[(unsigned)index];
640  CProp prop;
641  prop.Id = (unsigned)index;
642
643  if (IsLogSizeProp(prop.Id))
644  {
645    RINOK(PROPVARIANT_to_DictSize(value, prop.Value))
646  }
647  else
648  {
649    if (!ConvertProperty(value, nameToPropID.VarType, prop.Value))
650      return E_INVALIDARG;
651  }
652  Props.Add(prop);
653  return S_OK;
654}
655
656
657static UInt64 GetMemoryUsage_LZMA(UInt32 dict, bool isBt, UInt32 numThreads)
658{
659  UInt32 hs = dict - 1;
660  hs |= (hs >> 1);
661  hs |= (hs >> 2);
662  hs |= (hs >> 4);
663  hs |= (hs >> 8);
664  hs >>= 1;
665  if (hs >= (1 << 24))
666    hs >>= 1;
667  hs |= (1 << 16) - 1;
668  // if (numHashBytes >= 5)
669  if (!isBt)
670    hs |= (256 << 10) - 1;
671  hs++;
672  UInt64 size1 = (UInt64)hs * 4;
673  size1 += (UInt64)dict * 4;
674  if (isBt)
675    size1 += (UInt64)dict * 4;
676  size1 += (2 << 20);
677
678  if (numThreads > 1 && isBt)
679    size1 += (2 << 20) + (4 << 20);
680  return size1;
681}
682
683static const UInt32 kLzmaMaxDictSize = (UInt32)15 << 28;
684
685UInt64 CMethodProps::Get_Lzma_MemUsage(bool addSlidingWindowSize) const
686{
687  const UInt64 dicSize = Get_Lzma_DicSize();
688  const bool isBt = Get_Lzma_MatchFinder_IsBt();
689  const UInt32 dict32 = (dicSize >= kLzmaMaxDictSize ? kLzmaMaxDictSize : (UInt32)dicSize);
690  const UInt32 numThreads = Get_Lzma_NumThreads();
691  UInt64 size = GetMemoryUsage_LZMA(dict32, isBt, numThreads);
692
693  if (addSlidingWindowSize)
694  {
695    const UInt32 kBlockSizeMax = (UInt32)0 - (UInt32)(1 << 16);
696    UInt64 blockSize = (UInt64)dict32 + (1 << 16)
697        + (numThreads > 1 ? (1 << 20) : 0);
698    blockSize += (blockSize >> (blockSize < ((UInt32)1 << 30) ? 1 : 2));
699    if (blockSize >= kBlockSizeMax)
700      blockSize = kBlockSizeMax;
701    size += blockSize;
702  }
703  return size;
704}
705
706
707
708
709HRESULT COneMethodInfo::ParseMethodFromString(const UString &s)
710{
711  MethodName.Empty();
712  int splitPos = s.Find(L':');
713  {
714    UString temp = s;
715    if (splitPos >= 0)
716      temp.DeleteFrom((unsigned)splitPos);
717    if (!temp.IsAscii())
718      return E_INVALIDARG;
719    MethodName.SetFromWStr_if_Ascii(temp);
720  }
721  if (splitPos < 0)
722    return S_OK;
723  PropsString = s.Ptr((unsigned)(splitPos + 1));
724  return ParseParamsFromString(PropsString);
725}
726
727HRESULT COneMethodInfo::ParseMethodFromPROPVARIANT(const UString &realName, const PROPVARIANT &value)
728{
729  if (!realName.IsEmpty() && !StringsAreEqualNoCase_Ascii(realName, "m"))
730    return ParseParamsFromPROPVARIANT(realName, value);
731  // -m{N}=method
732  if (value.vt != VT_BSTR)
733    return E_INVALIDARG;
734  UString s;
735  s = value.bstrVal;
736  return ParseMethodFromString(s);
737}
738