bool CmdLineParser::_ParseRandomDistribution()

in CmdLineParser/CmdLineParser.cpp [580:716]


bool CmdLineParser::_ParseRandomDistribution(const char *arg, vector<Target>& vTargets)
{
    vector<DistributionRange> vOr;
    DistributionType dType;
    bool fOk = false;
    UINT32 pctAcc = 0, pctCur;          // accumulated/cur pct io
    UINT64 targetAcc = 0, targetCur;    // accumulated/cur target

    if (!strncmp(arg, "pct", 3))
    {
        dType = DistributionType::Percent;
    }
    else if (strncmp(arg, "abs", 3))
    {
        fprintf(stderr, "Unrecognized random distribution type\n");
        return false;
    }
    else
    {
        dType = DistributionType::Absolute;
    }

    arg += 3;

    //
    // Parse pairs of
    //
    //  * pct:  percentage/target percentage
    //  * abs:  percentage/absolute range of target
    //
    // Ex: 90/10:5/5 => [0,90) -> [0, 10) :: [90, 95) -> [10, 15)
    //  a remainder of [95, 100) -> [15, 100) would be applied.
    //
    // Percentages are cumulative and successively define the span of
    // the preceding definition. Absolute ranges are also cumulative:
    // 10/1G:90/1G puts 90% of accesses in the second 1G range of the
    // target.
    //
    // A single percentage can be 100 but is of limited value since it
    // would only be valid as a single element distribution.
    //
    // Basic numeric validations are done here (similar to XSD for XML).
    // Cross validation with other workload parameters (blocksize) and whole
    // distribution validation is delayed to common code.
    //

    while (true)
    {
        // Consume IO% integer
        fOk = Util::ParseUInt(arg, pctCur, arg);
        if (!fOk)
        {
            fprintf(stderr, "Invalid integer IO%%: must be > 0 and <= %u\n", 100 - pctAcc);
            return false;
        }
        // hole is ok
        else if (pctCur > 100)
        {
            fprintf(stderr, "Invalid IO%% %u: must be >= 0 and <= %u\n", pctCur, 100 - pctAcc);
            return false;
        }

        // Expect separator
        if (*arg++ != '/')
        {
            fprintf(stderr, "Expected / separator after %u\n", pctCur);
            return false;
        }

        // Consume Target%/Absolute range integer
        if (dType == DistributionType::Percent)
        {
            // Percent specification
            fOk = Util::ParseUInt(arg, targetCur, arg);
            if (!fOk)
            {
                fprintf(stderr, "Invalid integer Target%%: must be > 0 and <= %I64u\n", 100 - targetAcc);
                return false;
            }
            // no hole
            else if (targetCur == 0 || targetCur > 100)
            {
                fprintf(stderr, "Invalid Target%% %I64u: must be > 0 and <= %I64u\n", targetCur, 100 - targetAcc);
                return false;
            }
        }
        else
        {
            // Size specification
            fOk = CmdLineParser::_GetSizeInBytes(arg, targetCur, &arg);
            if (!fOk)
            {
                // error already emitted
                return fOk;
            }

            if (targetCur == 0)
            {
                fprintf(stderr, "Invalid zero length target range\n");
                return false;
            }
        }

        // Add range from [accumulator - accumulator + current) => ...
        // Note that zero pctCur indicates a hole where no IO is desired - this is recorded
        // for fidelity of display/profile but will never match on lookup, as intended.
        vOr.emplace_back(pctAcc, pctCur, make_pair(targetAcc, targetCur));

        // Now move accumulators for the next tuple/completion
        pctAcc += pctCur;
        targetAcc += targetCur;

        // Expect/consume separator for next tuple?
        if (*arg == ':')
        {
            ++arg;
            continue;
        }

        // Done?
        if (*arg == '\0')
        {
            break;
        }

        fprintf(stderr, "Unexpected characters in specification '%s'\n", arg);
        return false;
    }

    // Apply to all targets
    for (auto& t : vTargets)
    {
        t.SetDistributionRange(vOr, dType);
    }

    return true;
}