Przeglądaj źródła

Merge branch 'candidate-6.4.0'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 8 lat temu
rodzic
commit
bab5cec767

+ 19 - 0
common/deftype/deftype.cpp

@@ -2257,6 +2257,25 @@ bool isSingleValuedType(ITypeInfo * type)
     return false;
 }
 
+bool isStandardSizeInt(ITypeInfo * type)
+{
+    switch (type->getTypeCode())
+    {
+    case type_int:
+    case type_swapint:
+        switch (type->getSize())
+        {
+        case 1:
+        case 2:
+        case 4:
+        case 8:
+            return true;
+        }
+        break;
+    }
+    return false;
+}
+
 
 //============================================================================
 

+ 1 - 0
common/deftype/deftype.hpp

@@ -246,6 +246,7 @@ extern DEFTYPE_API bool isUnicodeType(ITypeInfo * type);
 extern DEFTYPE_API bool isLittleEndian(ITypeInfo * type);
 extern DEFTYPE_API bool isDatasetType(ITypeInfo * type);
 extern DEFTYPE_API bool isSingleValuedType(ITypeInfo * type);
+extern DEFTYPE_API bool isStandardSizeInt(ITypeInfo * type); // Is this an int type that can be represented by a c++ int?
 inline bool isFixedSize(ITypeInfo * type) { return type && (type->getSize() != UNKNOWN_LENGTH); }
 inline bool isUnknownSize(ITypeInfo * type) { return type && (type->getSize() == UNKNOWN_LENGTH); }
 inline bool isAnyType(ITypeInfo * type) { return type && (type->getTypeCode() == type_any); }

+ 17 - 3
dali/base/dadfs.cpp

@@ -9708,7 +9708,7 @@ bool removeClusterSpares(const char *clusterName, const char *type, SocketEndpoi
     return init.removeSpares(clusterName, type, eps, response);
 }
 
-IGroup *getClusterNodeGroup(const char *clusterName, const char *type, unsigned timems)
+static IGroup *getClusterNodeGroup(const char *clusterName, const char *type, bool processGroup, unsigned timems)
 {
     VStringBuffer clusterPath("/Environment/Software/%s[@name=\"%s\"]", type, clusterName);
     Owned<IRemoteConnection> conn = querySDS().connect(clusterPath.str(), myProcessSession(), RTM_LOCK_READ, SDS_CONNECT_TIMEOUT);
@@ -9734,11 +9734,25 @@ IGroup *getClusterNodeGroup(const char *clusterName, const char *type, unsigned
         throwStringExceptionV(0, "DFS cluster topology for '%s', does not match existing DFS group layout for group '%s'", clusterName, nodeGroupName.str());
     Owned<IGroup> clusterGroup = init.getGroupFromCluster(type, cluster, false);
     ICopyArrayOf<INode> nodes;
-    for (unsigned n=0; n<clusterGroup->ordinality(); n++)
-        nodes.append(nodeGroup->queryNode(n));
+    unsigned l=processGroup?cluster.getPropInt("@slavesPerNode", 1):1; // if process group requested, repeat clusterGroup slavesPerNode times.
+    for (unsigned t=0; t<l; t++)
+    {
+        for (unsigned n=0; n<clusterGroup->ordinality(); n++)
+            nodes.append(nodeGroup->queryNode(n));
+    }
     return createIGroup(nodes.ordinality(), nodes.getArray());
 }
 
+IGroup *getClusterNodeGroup(const char *clusterName, const char *type, unsigned timems)
+{
+    return getClusterNodeGroup(clusterName, type, false, timems);
+}
+
+IGroup *getClusterProcessNodeGroup(const char *clusterName, const char *type, unsigned timems)
+{
+    return getClusterNodeGroup(clusterName, type, true, timems);
+}
+
 
 class CDaliDFSServer: public Thread, public CTransactionLogTracker, implements IDaliServer, implements IExceptionHandler
 {  // Coven size

+ 2 - 1
dali/base/dadfs.hpp

@@ -788,7 +788,8 @@ extern da_decl bool removeClusterSpares(const char *clusterName, const char *typ
 // should poss. belong in lib workunit
 extern da_decl StringBuffer &getClusterGroupName(IPropertyTree &cluster, StringBuffer &groupName);
 extern da_decl StringBuffer &getClusterSpareGroupName(IPropertyTree &cluster, StringBuffer &groupName);
-extern da_decl IGroup *getClusterNodeGroup(const char *clusterName, const char *type, unsigned timems=INFINITE);
+extern da_decl IGroup *getClusterNodeGroup(const char *clusterName, const char *type, unsigned timems=INFINITE); // returns the raw cluster group (as defined in the Cluster topology)
+extern da_decl IGroup *getClusterProcessNodeGroup(const char *clusterName, const char *type, unsigned timems=INFINITE); // returns the group of all processes of cluster group (i.e. cluster group * slavesPerNode)
 
 extern da_decl IDistributedFileTransaction *createDistributedFileTransaction(IUserDescriptor *user, ICodeContext *ctx=NULL);
 

+ 2 - 0
ecl/hql/hqlatoms.cpp

@@ -145,6 +145,7 @@ IAtom * _dot_Atom;
 IAtom * dynamicAtom;
 IAtom * ebcdicAtom;
 IAtom * eclrtlAtom;
+IAtom * embedAtom;
 IAtom * embeddedAtom;
 IAtom * _empty_str_Atom;
 IAtom * encodingAtom;
@@ -599,6 +600,7 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(dynamic);
     MAKEATOM(ebcdic);
     MAKEATOM(eclrtl);
+    MAKEATOM(embed);
     MAKEATOM(embedded);
     _empty_str_Atom = createAtom("");
     MAKEATOM(encoding);

+ 1 - 0
ecl/hql/hqlatoms.hpp

@@ -149,6 +149,7 @@ extern HQL_API IAtom * _dot_Atom;
 extern HQL_API IAtom * dynamicAtom;
 extern HQL_API IAtom * ebcdicAtom;
 extern HQL_API IAtom * eclrtlAtom;
+extern HQL_API IAtom * embedAtom;
 extern HQL_API IAtom * embeddedAtom;
 extern HQL_API IAtom * _empty_str_Atom;
 extern HQL_API IAtom * encodingAtom;

+ 2 - 2
ecl/hql/hqldesc.cpp

@@ -89,7 +89,7 @@ static void expandRecordSymbolsMeta(IPropertyTree * metaTree, IHqlExpression * r
         case no_field:
             {
                 IPropertyTree * field = metaTree->addPropTree("Field", createPTree("Field"));
-                field->setProp("@name", str(cur->queryName()));
+                field->setProp("@name", str(cur->queryId()));
                 StringBuffer ecltype;
                 cur->queryType()->getECLType(ecltype);
                 field->setProp("@type", ecltype);
@@ -134,7 +134,7 @@ void expandSymbolMeta(IPropertyTree * metaTree, IHqlExpression * expr)
         if (javadoc)
             def->addPropTree("Documentation", javadoc.getClear());
         IHqlNamedAnnotation * symbol = queryNameAnnotation(expr);
-        def->setProp("@name", str(expr->queryName()));
+        def->setProp("@name", str(expr->queryId()));
         def->setPropInt("@line", expr->getStartLine());
         if (expr->isExported())
             def->setPropBool("@exported", true);

+ 1 - 1
ecl/hql/hqlir.cpp

@@ -1906,6 +1906,7 @@ id_t ExpressionIRPlayer::doProcessType(ITypeInfo * type)
         case type_dictionary:
         case type_function:
         case type_pointer:
+        case type_array:
             {
                 CompoundTypeBuilderInfo info;
                 info.baseType = processType(type->queryChildType());
@@ -1917,7 +1918,6 @@ id_t ExpressionIRPlayer::doProcessType(ITypeInfo * type)
         case type_ifblock:
         case type_alias:
         case type_blob:
-        case type_array:
             throwUnexpected();
             break;
         case type_class:

+ 27 - 6
ecl/hql/hqllex.l

@@ -37,7 +37,7 @@
 //#define DEBUG_TOKEN 
 
 #ifdef DEBUG_TOKEN
-#define TraceReturnToken(id) PrintLog("Returning " #id " %s", name->str())
+#define TraceReturnToken(id) PrintLog("Returning " #id " %s", str(name))
 #else 
 #define TraceReturnToken(id)
 #endif
@@ -1447,6 +1447,8 @@ FUNCTIONMACRO|MACRO {
                         int endpos = startpos;
                         bool isFunctionMacro = (CUR_TOKEN_LENGTH != 5);
                         lexer->macroGathering++;
+                        bool inEmbed = false;
+                        unsigned embedParens = 0;
                         while ((tok = lexer->yyLex(returnToken, false, activeState)) != ENDMACRO)
                         {
                             if (tok==EOF)
@@ -1460,6 +1462,22 @@ FUNCTIONMACRO|MACRO {
                                 complex = true;
                             else if (tok == '@')
                                 isFunctionMacro = true;
+                            else if (tok == UNKNOWN_ID && returnToken.queryId()->queryLower()==embedAtom)
+                                inEmbed = true;
+                            if (inEmbed)
+                            {
+                                if (tok=='(')
+                                    embedParens++;
+                                else if (tok==')')
+                                {
+                                    embedParens--;
+                                    if (!embedParens)
+                                    {
+                                        BEGIN(CPP);
+                                        inEmbed = false;
+                                    }
+                                }
+                            }
                             returnToken.release();
                         }
                         lexer->macroGathering--;
@@ -1476,6 +1494,9 @@ FUNCTIONMACRO|MACRO {
                         if (!isFunctionMacro)
                         {
                             macroContents.setown(createFileContentsFromText(len, lexer->yyBuffer+startpos, lexer->sourcePath, lexer->yyParser->inSignedModule, lexer->yyParser->gpgSignature));
+#if defined(TRACE_TOKEN)
+                            PrintLog("TOKEN>> macro body read: \"%.*s\"", len, lexer->yyBuffer+startpos);
+#endif
                         }
                         else
                         {
@@ -1490,12 +1511,12 @@ FUNCTIONMACRO|MACRO {
                             memcpy(macroBuf + lenPrefix, lexer->yyBuffer+startpos, len);
                             memcpy(macroBuf + lenPrefix + len, suffix, lenSuffix);
                             macroContents.setown(createFileContentsFromText(lenSuffix + len + lenPrefix, macroBuf, lexer->sourcePath,lexer->yyParser->inSignedModule, lexer->yyParser->gpgSignature));
+#if defined(TRACE_TOKEN)
+                            PrintLog("TOKEN>> macro body read: \"%.*s\"",len+lenPrefix+lenSuffix, macroBuf);
+#endif
                             free(macroBuf);
                         }
 
-#if defined(TRACE_TOKEN)
-                        PrintLog("TOKEN>> macro body read: \"%s\"",macroBuf);
-#endif
 
                         returnToken.setContents(macroContents.getClear());
                         returnToken.setPosition(startLocation);
@@ -1891,6 +1912,6 @@ FUNCTIONMACRO|MACRO {
 %%
 void HqlLex::doEnterEmbeddedMode(yyscan_t yyscanner)
 {
-   struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
-   BEGIN(CPP);
+    struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+    BEGIN(CPP);
 }

+ 7 - 2
ecl/hql/hqlparse.cpp

@@ -2505,8 +2505,13 @@ unsigned HqlLex::getTypeSize(unsigned lengthTypeName)
 
 void HqlLex::enterEmbeddedMode()
 {
-    doEnterEmbeddedMode(scanner);
-    inCpp = true;
+    if (inmacro)
+        inmacro->enterEmbeddedMode();
+    else
+    {
+        doEnterEmbeddedMode(scanner);
+        inCpp = true;
+    }
 }
 
 int HqlLex::yyLex(YYSTYPE & returnToken, bool lookup, const short * activeState)

+ 8 - 2
ecl/hqlcpp/hqlcpp.ipp

@@ -367,12 +367,12 @@ public:
     void setDefault(IHqlExpression * expr);
 
 protected:
-    bool canBuildStaticList(ITypeInfo * type) { return isFixedSize(type); }
+    bool canBuildStaticList(ITypeInfo * type);
 
     void buildChop3Map(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test, IHqlExpression * temp, unsigned start, unsigned end);
     void buildChop3Map(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test);
     void buildChop2Map(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test, unsigned start, unsigned end);
-    IHqlExpression * buildIndexedMap(BuildCtx & ctx, IHqlExpression * test, unsigned lower, unsigned upper);
+    IHqlExpression * buildIndexedMap(BuildCtx & ctx, const CHqlBoundExpr & test);
     void buildLoopChopMap(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test);
     void buildIntegerSearchMap(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * est);
     void buildSwitchCondition(BuildCtx & ctx, CHqlBoundExpr & bound);
@@ -405,9 +405,15 @@ protected:
     HqlExprAttr             defaultValue;
     HqlExprArray            pairs;
     HqlExprArray            originalPairs;
+    OwnedHqlExpr lowestCompareExpr;
+    OwnedHqlExpr highestCompareExpr;
+    OwnedHqlExpr lowerTableBound;
+    OwnedHqlExpr upperTableBound;
     bool complexCompare;
     bool constantCases;
     bool constantValues;
+    bool useRangeIndex = false;
+    bool allResultsMatch = true;
     OwnedITypeInfo resultType;
     OwnedITypeInfo indexType;
     OwnedITypeInfo promotedElementType;

+ 158 - 66
ecl/hqlcpp/hqlcppcase.cpp

@@ -37,6 +37,7 @@
 #define MAX_NUM_NOBREAK_CASE                        80      // maximum number of case: without a break - compiler workaround
 #define INLINE_COMPARE_THRESHOLD                    2       // above this, a loop is generated
 #define SWITCH_TABLE_DENSITY_THRESHOLD              3       // % used before use array index.
+#define RANGE_DENSITY_THRESHOLD                     30      // % used before to use array index with range checking
 #define MAX_NESTED_CASES                            8       // to stop C++ compiler running out of scopes.
 
 //===========================================================================
@@ -115,9 +116,14 @@ IHqlExpression * createNotFoundValue()
 
 //===========================================================================
 
+static int compareValues(IHqlExpression * lexpr, IHqlExpression * rexpr)
+{
+    return lexpr->queryValue()->compare(rexpr->queryValue());
+}
+
 static int comparePair(IHqlExpression * lexpr, IHqlExpression * rexpr)
 {
-    return lexpr->queryChild(0)->queryValue()->compare(rexpr->queryChild(0)->queryValue());
+    return compareValues(lexpr->queryChild(0), rexpr->queryChild(0));
 }
 
 static int comparePair(IInterface * const * left, IInterface * const * right)
@@ -137,22 +143,39 @@ HqlCppCaseInfo::HqlCppCaseInfo(HqlCppTranslator & _translator) : translator(_tra
 
 void HqlCppCaseInfo::addPair(IHqlExpression * expr)
 {
+    IHqlExpression * compareExpr = expr->queryChild(0);
+    IHqlExpression * resultExpr = expr->queryChild(1);
+    if (allResultsMatch && pairs.ordinality())
+    {
+        if (pairs.tos().queryChild(1) != resultExpr)
+            allResultsMatch = false;
+    }
+
     pairs.append(*LINK(expr));
     
-    IHqlExpression * compareValue = expr->queryChild(0);
-    if (!compareValue->queryValue())
+    if (!compareExpr->queryValue())
+    {
         constantCases = false;
+    }
+    else if (constantCases)
+    {
+        if (!lowestCompareExpr || compareValues(compareExpr, lowestCompareExpr) < 0)
+            lowestCompareExpr.set(compareExpr);
+        if (!highestCompareExpr || compareValues(compareExpr, highestCompareExpr) > 0)
+            highestCompareExpr.set(compareExpr);
+    }
+
     if (!expr->queryChild(1)->queryValue())
         constantValues = false;
 
     if (cond && !complexCompare)
     {
-        ITypeInfo * valueType = compareValue->queryType();
+        ITypeInfo * valueType = compareExpr->queryType();
         if (valueType != cond->queryType())
-            complexCompare = isCompare3Valued(compareValue->queryType());
+            complexCompare = isCompare3Valued(compareExpr->queryType());
     }
 
-    updateResultType(expr->queryChild(1));
+    updateResultType(resultExpr);
 
 }
 
@@ -163,6 +186,19 @@ void HqlCppCaseInfo::addPairs(HqlExprArray & _pairs)
         addPair(&_pairs.item(idx));
 }
 
+bool HqlCppCaseInfo::canBuildStaticList(ITypeInfo * type)
+{
+    switch (type->getTypeCode())
+    {
+    case type_int:
+        return isStandardSizeInt(type);
+    case type_swapint:
+        return false;
+    default:
+        return isFixedSize(type);
+    }
+}
+
 bool HqlCppCaseInfo::buildAssign(BuildCtx & ctx, const CHqlBoundTarget & target)
 {
     if (pairs.ordinality() == 0)
@@ -362,80 +398,121 @@ void HqlCppCaseInfo::buildChop2Map(BuildCtx & ctx, const CHqlBoundTarget & targe
     }
 }
 
-IHqlExpression * HqlCppCaseInfo::buildIndexedMap(BuildCtx & ctx, IHqlExpression * test, unsigned lower, unsigned upper)
+IHqlExpression * HqlCppCaseInfo::buildIndexedMap(BuildCtx & ctx, const CHqlBoundExpr & test)
 {
-    ITypeInfo * compareType = test->queryType()->queryPromotedType();
+    ITypeInfo * compareType = test.queryType()->queryPromotedType();
     type_t compareTypeCode = compareType->getTypeCode();
 
     HqlExprArray values;
     IHqlExpression * dft = queryActiveTableSelector();  // value doesn't matter as long as it will not occur
-    unsigned num = (upper-lower+1);
-    values.ensure(num);
-    unsigned idx;
-    for (idx = 0; idx < num; idx++)
-        values.append(*LINK(dft));
+    __int64 lower = getIntValue(lowerTableBound, 0);
+    unsigned num = (getIntValue(upperTableBound, 0)-lower)+1;
 
-    ForEachItemIn(idx2, pairs)
+    CHqlBoundExpr indexExpr;
+    switch (compareTypeCode)
     {
-        IHqlExpression & cur = pairs.item(idx2);
-        IValue * value = cur.queryChild(0)->queryValue();
-        unsigned replaceIndex;
-        switch (compareTypeCode)
-        {
         case type_int:
-            replaceIndex = (int)value->getIntValue()-lower;
+            indexExpr.set(test);
             break;
         case type_string:
-            {
-                StringBuffer temp;
-                value->getStringValue(temp);
-                replaceIndex = (int)(unsigned char)temp.charAt(0)-lower;
-                break;
-            }
+            indexExpr.expr.setown(createValue(no_index, makeCharType(), LINK(test.expr), getZero()));
+            indexExpr.expr.setown(createValue(no_cast, makeIntType(1, false), LINK(indexExpr.expr)));
+            break;
         default:
             throwUnexpectedType(compareType);
-        }
-
-        IHqlExpression * mapTo = cur.queryChild(1);
-        if (mapTo->getOperator() != no_constant)
-            throwUnexpected();
-        if (replaceIndex >= num)
-            translator.reportWarning(CategoryIgnored, HQLWRN_CaseCanNeverMatch, "CASE entry %d can never match the test condition", replaceIndex);
-        else
-            values.replace(*LINK(mapTo),replaceIndex);
     }
 
-    //Now replace the placeholders with the default values.
-    for (idx = 0; idx < num; idx++)
+    if (useRangeIndex && (num != 1))
+        translator.ensureSimpleExpr(ctx, indexExpr);
+
+    OwnedHqlExpr mapped;
+    ITypeInfo * retType = resultType;
+    //if num == pairs.ordinality() and all results are identical, avoid the table lookup.
+    if (allResultsMatch && (num == pairs.ordinality()))
     {
-        if (&values.item(idx) == dft)
-            values.replace(*defaultValue.getLink(),idx);
+        mapped.set(pairs.item(0).queryChild(1));
     }
+    else
+    {
+        values.ensure(num);
+        unsigned idx;
+        for (idx = 0; idx < num; idx++)
+            values.append(*LINK(dft));
 
-    // use a var string type to get better C++ generated...
-    ITypeInfo * storeType = getArrayElementType(resultType);
-    ITypeInfo * listType = makeArrayType(storeType, values.ordinality());
-    OwnedHqlExpr lvalues = createValue(no_list, listType, values);
+        ForEachItemIn(idx2, pairs)
+        {
+            IHqlExpression & cur = pairs.item(idx2);
+            IValue * value = cur.queryChild(0)->queryValue();
+            unsigned replaceIndex;
+            switch (compareTypeCode)
+            {
+            case type_int:
+                replaceIndex = (unsigned)(value->getIntValue()-lower);
+                break;
+            case type_string:
+                {
+                    StringBuffer temp;
+                    value->getStringValue(temp);
+                    replaceIndex = (unsigned)((unsigned char)temp.charAt(0)-lower);
+                    break;
+                }
+            default:
+                throwUnexpectedType(compareType);
+            }
 
-    CHqlBoundExpr boundTable;
-    translator.buildExpr(ctx, lvalues, boundTable);
-    IHqlExpression * tableIndex;
-    switch (compareTypeCode)
+            IHqlExpression * mapTo = cur.queryChild(1);
+            if (mapTo->getOperator() != no_constant)
+                throwUnexpected();
+            if (replaceIndex >= num)
+                translator.reportWarning(CategoryIgnored, HQLWRN_CaseCanNeverMatch, "CASE entry %d can never match the test condition", replaceIndex);
+            else
+                values.replace(*LINK(mapTo),replaceIndex);
+        }
+
+        //Now replace the placeholders with the default values.
+        for (idx = 0; idx < num; idx++)
+        {
+            if (&values.item(idx) == dft)
+                values.replace(*defaultValue.getLink(),idx);
+        }
+
+        // use a var string type to get better C++ generated...
+        ITypeInfo * storeType = getArrayElementType(resultType);
+        ITypeInfo * listType = makeArrayType(storeType, values.ordinality());
+        OwnedHqlExpr lvalues = createValue(no_list, listType, values);
+
+        CHqlBoundExpr boundTable;
+        translator.buildExpr(ctx, lvalues, boundTable);
+
+        LinkedHqlExpr tableIndex = indexExpr.expr;
+        if (getIntValue(lowerTableBound, 0))
+            tableIndex.setown(createValue(no_sub, tableIndex->getType(), LINK(tableIndex), LINK(lowerTableBound)));
+
+        IHqlExpression * ret = createValue(no_index, LINK(retType), LINK(boundTable.expr), LINK(tableIndex));
+        mapped.setown(createTranslatedOwned(ret));
+    }
+
+    if (useRangeIndex)
     {
-        case type_int:
-            tableIndex = LINK(test);
-            break;
-        case type_string:
-            tableIndex = createValue(no_index, makeCharType(), LINK(test), getZero());
-            tableIndex = createValue(no_cast, makeIntType(1, false), tableIndex);
-            break;
-        default:
-            throwUnexpectedType(compareType);
+        if (num != 1)
+        {
+            OwnedHqlExpr testValue = indexExpr.getTranslatedExpr();
+            OwnedHqlExpr aboveLower = createCompare(no_ge, testValue, lowestCompareExpr);
+            OwnedHqlExpr belowUpper = createCompare(no_le, testValue, highestCompareExpr);
+            OwnedHqlExpr inRange = createValue(no_and, makeBoolType(), aboveLower.getClear(), belowUpper.getClear());
+            mapped.setown(createValue(no_if, LINK(retType), inRange.getClear(), LINK(mapped), LINK(defaultValue)));
+        }
+        else
+        {
+            assertex(allResultsMatch);
+            OwnedHqlExpr testValue = indexExpr.getTranslatedExpr();
+            OwnedHqlExpr inRange = createCompare(no_eq, testValue, lowestCompareExpr);
+            mapped.setown(createValue(no_if, LINK(retType), inRange.getClear(), LINK(mapped), LINK(defaultValue)));
+        }
     }
 
-    ITypeInfo * retType = resultType;
-    IHqlExpression * ret = createValue(no_index, LINK(retType), LINK(boundTable.expr), tableIndex);
-    return createTranslatedOwned(ret);
+    return mapped.getClear();
+
 }
 
 void HqlCppCaseInfo::buildLoopChopMap(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test)
@@ -1038,20 +1115,40 @@ void HqlCppCaseInfo::promoteTypes()
 
 bool HqlCppCaseInfo::canBuildArrayLookup(const CHqlBoundExpr & test)
 {
+    if (!constantValues)
+        return false;
+
     ITypeInfo * condType = test.queryType()->queryPromotedType();
 
     //MORE: Also support this for high density tables that don't start at 0... - checking upper and lower bounds
     unsigned bitSize = condType->getBitSize();
-    if (constantValues && (bitSize && (bitSize <= 8) && !condType->isSigned()))
+    if ((bitSize && (bitSize <= 8) && !condType->isSigned()))
     {
         unsigned limit = (1 << bitSize);
         //use case if enough items, or above a certain density...
         if (pairs.ordinality() * 100 >= limit * SWITCH_TABLE_DENSITY_THRESHOLD)
         {
             if ((condType->getTypeCode() == type_int) || (condType->getTypeCode() == type_string))
+            {
+                lowerTableBound.setown(getSizetConstant(0));
+                upperTableBound.setown(getSizetConstant(limit-1));
                 return true;
+            }
         }
     }
+
+    if (condType->isInteger())
+    {
+        unsigned __int64 range = getIntValue(highestCompareExpr, 0) - getIntValue(lowestCompareExpr, 0) + 1;
+        if (pairs.ordinality() * 100 >= range  * RANGE_DENSITY_THRESHOLD)
+        {
+            useRangeIndex = true;
+            lowerTableBound.set(lowestCompareExpr);
+            upperTableBound.set(highestCompareExpr);
+            return true;
+        }
+    }
+
     return false;
 }
 
@@ -1059,13 +1156,8 @@ bool HqlCppCaseInfo::queryBuildArrayLookup(BuildCtx & ctx, const CHqlBoundTarget
 {
     if (canBuildArrayLookup(test) && canBuildStaticList(resultType) && defaultValue->isConstant())
     {
-        //MORE: Also support this for high density tables that don't start at 0... - checking upper and lower bounds
-        ITypeInfo * condType = test.queryType()->queryPromotedType();
-        unsigned bitSize = condType->getBitSize();
-        unsigned limit = (1 << bitSize);
-
         BuildCtx subctx(ctx);
-        OwnedHqlExpr ret = buildIndexedMap(subctx, test.expr, 0, limit-1);
+        OwnedHqlExpr ret = buildIndexedMap(subctx, test);
         translator.buildExprAssign(ctx, target, ret);
         return true;
     }

+ 6 - 3
ecl/hqlcpp/hqlhtcpp.cpp

@@ -17186,7 +17186,10 @@ ABoundActivity * HqlCppTranslator::doBuildActivityTempTable(BuildCtx & ctx, IHql
                 normalized.setown(spotScalarCSE(normalized, NULL, queryOptions().spotCseInIfDatasetConditions));
 
             if (normalized->getOperator() == no_alias)
-                buildExpr(instance->startctx, normalized, bound);
+            {
+                buildExpr(funcctx, normalized, bound);
+                rowsExpr.setown(createValue(no_countlist, makeIntType(8, false), LINK(normalized)));
+            }
             else
             {
                 BuildCtx * declarectx;
@@ -17198,9 +17201,9 @@ ABoundActivity * HqlCppTranslator::doBuildActivityTempTable(BuildCtx & ctx, IHql
                 CHqlBoundTarget tempTarget;
                 buildTempExpr(*callctx, *declarectx, tempTarget, normalized, FormatNatural, !canSetBeAll(normalized));
                 bound.setFromTarget(tempTarget);
+                rowsExpr.setown(getBoundCount(bound));
+                rowsExpr.setown(createTranslated(rowsExpr));
             }
-            rowsExpr.setown(getBoundCount(bound));
-            rowsExpr.setown(createTranslated(rowsExpr));
 
             OwnedHqlExpr compare = createValue(no_ge, makeBoolType(), LINK(rowVar), ensureExprType(rowsExpr, rowVar->queryType()));
             BuildCtx condctx(func.ctx);

+ 49 - 0
ecl/regress/dan3.ecl

@@ -0,0 +1,49 @@
+SomeFunc() := FUNCTIONMACRO
+
+/*
+    STRING MarkNodes(STRING l, STRING l_nodes, STRING n, STRING n_nodes) := BEGINC++
+        #option pure
+        #body
+        unsigned long i = 0;
+        char * out = (char*)rtlMalloc(lenN_nodes);
+        memcpy(out, n_nodes, lenN_nodes);
+        while ( i < lenN){
+                if (l[i] != n[i] || i >= lenL){
+                    out[i] = '1';
+                    break;
+                }
+                if (l_nodes[i] != n_nodes[i]){
+                    out[i] = '1';
+                }
+                i += 1;
+        }
+        __lenResult = lenN_nodes;
+        __result = out;
+    ENDC++;
+*/
+
+    STRING MarkNodes(STRING l, STRING l_nodes, STRING n, STRING n_nodes) := EMBED(C++ : DISTRIBUTED)
+        #option pure
+        #body
+        unsigned long i = 0;
+        char * out = (char*)rtlMalloc(lenN_nodes);
+        memcpy(out, n_nodes, lenN_nodes);
+        while ( i < lenN){
+                if (l[i] != n[i] || i >= lenL){
+                    out[i] = '1';
+                    break;
+                }
+                if (l_nodes[i] != n_nodes[i]){
+                    out[i] = '1';
+                }
+                i += 1;
+        }
+        __lenResult = lenN_nodes;
+        __result = out;
+    ENDEMBED;
+
+
+    RETURN MarkNodes('v1', 'v2', 'v3', 'v4');
+ENDMACRO;
+
+SomeFunc();

+ 51 - 2
system/jlib/jmutex.hpp

@@ -875,10 +875,10 @@ public:
 
 /* Usage example
 
+    static void *sobj = NULL;
+    static CSingletonLock slock;
     void *get()
     {
-        static void *sobj = NULL;
-        static CSingletonLock slock;
         if (slock.lock()) {
             if (!sobj)          // required
                 sobj = createSObj();
@@ -888,4 +888,53 @@ public:
     }
 */
 
+/*
+ * A template function for implementing a singleton object.  Using the same example as above would require:
+
+    static std::atomic<void *> sobj;
+    static CCriticalSection slock;
+    void *get()
+    {
+        return querySingleton(sobj, slock, []{ return createSObj; });
+    }
+
+ */
+
+template <typename X, typename FUNC>
+inline X * querySingleton(std::atomic<X *> & singleton, CriticalSection & cs, FUNC factory)
+{
+    X * value = singleton.load(std::memory_order_acquire);
+    if (value)
+        return value; // avoid crit
+
+    CriticalBlock block(cs);
+    value = singleton.load(std::memory_order_acquire);  // reload in case another thread got here first
+    if (!value)
+    {
+        value = factory();
+        singleton.store(value, std::memory_order_release);
+    }
+    return value;
+}
+
+/*
+ * A template class for implementing a singleton object.  Using the same example as above would require:
+
+    static Singleton<void> sobj;
+    void *get()
+    {
+        return sobj.query([]{ return createSObj; });
+    }
+
+ */
+template <typename X>
+class Singleton
+{
+public:
+    template <typename FUNC> X * query(FUNC factory) { return querySingleton(singleton, cs, factory); }
+private:
+    std::atomic<X *> singleton{nullptr};
+    CriticalSection cs;
+};
+
 #endif