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 ¶m, 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 ¶m = 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