1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 */ 9 10 #include <ostream> 11 #include <set> 12 13 #include <formula/FormulaCompiler.hxx> 14 #include <formula/grammar.hxx> 15 #include <formula/opcode.hxx> 16 #include <rtl/ustring.hxx> 17 #include <sal/log.hxx> 18 #include <sfx2/objsh.hxx> 19 #include <unotools/configmgr.hxx> 20 21 #include <calcconfig.hxx> 22 #include <compiler.hxx> 23 #include <docsh.hxx> 24 25 #include <comphelper/configurationlistener.hxx> 26 #include <com/sun/star/datatransfer/XTransferable2.hpp> 27 28 using comphelper::ConfigurationListener; 29 30 static rtl::Reference<ConfigurationListener> const & getMiscListener() 31 { 32 static rtl::Reference<ConfigurationListener> xListener(new ConfigurationListener("/org.openoffice.Office.Common/Misc")); 33 return xListener; 34 } 35 36 static rtl::Reference<ConfigurationListener> const & getFormulaCalculationListener() 37 { 38 static rtl::Reference<ConfigurationListener> xListener(new ConfigurationListener("/org.openoffice.Office.Calc/Formula/Calculation")); 39 return xListener; 40 } 41 42 static ForceCalculationType forceCalculationTypeInit() 43 { 44 const char* env = getenv( "SC_FORCE_CALCULATION" ); 45 if( env != nullptr ) 46 { 47 if( strcmp( env, "opencl" ) == 0 ) 48 { 49 SAL_INFO("sc.core.formulagroup", "Forcing calculations to use OpenCL"); 50 return ForceCalculationOpenCL; 51 } 52 if( strcmp( env, "threads" ) == 0 ) 53 { 54 SAL_INFO("sc.core.formulagroup", "Forcing calculations to use threads"); 55 return ForceCalculationThreads; 56 } 57 if( strcmp( env, "core" ) == 0 ) 58 { 59 SAL_INFO("sc.core.formulagroup", "Forcing calculations to use core"); 60 return ForceCalculationCore; 61 } 62 SAL_WARN("sc.core.formulagroup", "Unrecognized value of SC_FORCE_CALCULATION"); 63 abort(); 64 } 65 return ForceCalculationNone; 66 } 67 68 ForceCalculationType ScCalcConfig::getForceCalculationType() 69 { 70 static const ForceCalculationType type = forceCalculationTypeInit(); 71 return type; 72 } 73 74 bool ScCalcConfig::isOpenCLEnabled() 75 { 76 if (utl::ConfigManager::IsFuzzing()) 77 return false; 78 static ForceCalculationType force = getForceCalculationType(); 79 if( force != ForceCalculationNone ) 80 return force == ForceCalculationOpenCL; 81 static comphelper::ConfigurationListenerProperty<bool> gOpenCLEnabled(getMiscListener(), "UseOpenCL"); 82 return gOpenCLEnabled.get(); 83 } 84 85 bool ScCalcConfig::isThreadingEnabled() 86 { 87 if (utl::ConfigManager::IsFuzzing()) 88 return false; 89 static ForceCalculationType force = getForceCalculationType(); 90 if( force != ForceCalculationNone ) 91 return force == ForceCalculationThreads; 92 static comphelper::ConfigurationListenerProperty<bool> gThreadingEnabled(getFormulaCalculationListener(), "UseThreadedCalculationForFormulaGroups"); 93 return gThreadingEnabled.get(); 94 } 95 96 ScCalcConfig::ScCalcConfig() : 97 meStringRefAddressSyntax(formula::FormulaGrammar::CONV_UNSPECIFIED), 98 meStringConversion(StringConversion::LOCALE), // old LibreOffice behavior 99 mbEmptyStringAsZero(false), 100 mbHasStringRefSyntax(false) 101 { 102 setOpenCLConfigToDefault(); 103 104 // SAL _DEBUG(__FILE__ ":" << __LINE__ << ": ScCalcConfig::ScCalcConfig(): " << *this); 105 } 106 107 void ScCalcConfig::setOpenCLConfigToDefault() 108 { 109 // Keep in order of opcode value, is that clearest? (Random order, 110 // at least, would make no sense at all.) 111 static const OpCodeSet pDefaultOpenCLSubsetOpCodes(new o3tl::sorted_vector<OpCode>({ 112 ocAdd, 113 ocSub, 114 ocNegSub, 115 ocMul, 116 ocDiv, 117 ocPow, 118 ocRandom, 119 ocSin, 120 ocCos, 121 ocTan, 122 ocArcTan, 123 ocExp, 124 ocLn, 125 ocSqrt, 126 ocStdNormDist, 127 ocSNormInv, 128 ocRound, 129 ocPower, 130 ocSumProduct, 131 ocMin, 132 ocMax, 133 ocSum, 134 ocProduct, 135 ocAverage, 136 ocCount, 137 ocVar, 138 ocNormDist, 139 ocVLookup, 140 ocCorrel, 141 ocCovar, 142 ocPearson, 143 ocSlope, 144 ocSumIfs})); 145 146 // Note that these defaults better be kept in sync with those in 147 // officecfg/registry/schema/org/openoffice/Office/Calc.xcs. 148 // Crazy. 149 mbOpenCLSubsetOnly = true; 150 mbOpenCLAutoSelect = true; 151 mnOpenCLMinimumFormulaGroupSize = 100; 152 mpOpenCLSubsetOpCodes = pDefaultOpenCLSubsetOpCodes; 153 } 154 155 void ScCalcConfig::reset() 156 { 157 *this = ScCalcConfig(); 158 } 159 160 void ScCalcConfig::MergeDocumentSpecific( const ScCalcConfig& r ) 161 { 162 // String conversion options are per document. 163 meStringConversion = r.meStringConversion; 164 mbEmptyStringAsZero = r.mbEmptyStringAsZero; 165 // INDIRECT ref syntax is per document. 166 meStringRefAddressSyntax = r.meStringRefAddressSyntax; 167 mbHasStringRefSyntax = r.mbHasStringRefSyntax; 168 } 169 170 void ScCalcConfig::SetStringRefSyntax( formula::FormulaGrammar::AddressConvention eConv ) 171 { 172 meStringRefAddressSyntax = eConv; 173 mbHasStringRefSyntax = true; 174 } 175 176 bool ScCalcConfig::operator== (const ScCalcConfig& r) const 177 { 178 return meStringRefAddressSyntax == r.meStringRefAddressSyntax && 179 meStringConversion == r.meStringConversion && 180 mbEmptyStringAsZero == r.mbEmptyStringAsZero && 181 mbHasStringRefSyntax == r.mbHasStringRefSyntax && 182 mbOpenCLSubsetOnly == r.mbOpenCLSubsetOnly && 183 mbOpenCLAutoSelect == r.mbOpenCLAutoSelect && 184 maOpenCLDevice == r.maOpenCLDevice && 185 mnOpenCLMinimumFormulaGroupSize == r.mnOpenCLMinimumFormulaGroupSize && 186 *mpOpenCLSubsetOpCodes == *r.mpOpenCLSubsetOpCodes; 187 } 188 189 bool ScCalcConfig::operator!= (const ScCalcConfig& r) const 190 { 191 return !operator==(r); 192 } 193 194 OUString ScOpCodeSetToSymbolicString(const ScCalcConfig::OpCodeSet& rOpCodes) 195 { 196 OUStringBuffer result(256); 197 formula::FormulaCompiler aCompiler; 198 formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH)); 199 200 for (auto i = rOpCodes->begin(); i != rOpCodes->end(); ++i) 201 { 202 if (i != rOpCodes->begin()) 203 result.append(';'); 204 result.append(pOpCodeMap->getSymbol(*i)); 205 } 206 207 return result.toString(); 208 } 209 210 ScCalcConfig::OpCodeSet ScStringToOpCodeSet(const OUString& rOpCodes) 211 { 212 ScCalcConfig::OpCodeSet result = std::make_shared<o3tl::sorted_vector< OpCode >>(); 213 formula::FormulaCompiler aCompiler; 214 formula::FormulaCompiler::OpCodeMapPtr pOpCodeMap(aCompiler.GetOpCodeMap(css::sheet::FormulaLanguage::ENGLISH)); 215 216 const formula::OpCodeHashMap& rHashMap(pOpCodeMap->getHashMap()); 217 218 sal_Int32 fromIndex(0); 219 sal_Int32 semicolon; 220 OUString s(rOpCodes + ";"); 221 222 while ((semicolon = s.indexOf(';', fromIndex)) >= 0) 223 { 224 if (semicolon > fromIndex) 225 { 226 OUString element(s.copy(fromIndex, semicolon - fromIndex)); 227 sal_Int32 n = element.toInt32(); 228 if (n > 0 || (n == 0 && element == "0")) 229 result->insert(static_cast<OpCode>(n)); 230 else 231 { 232 auto opcode(rHashMap.find(element)); 233 if (opcode != rHashMap.end()) 234 result->insert(opcode->second); 235 else 236 SAL_WARN("sc.opencl", "Unrecognized OpCode " << element << " in OpCode set string"); 237 } 238 } 239 fromIndex = semicolon+1; 240 } 241 // HACK: Both unary and binary minus have the same string but different opcodes. 242 if( result->find( ocSub ) != result->end()) 243 result->insert( ocNegSub ); 244 return result; 245 } 246 247 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 248
