8aaa28ed43
This allows to make sure we actually use sorted which ranges, and then it's safe to call SfxItemSet::MergeRange when needed. Also this change relaxes the previous requirement that ranges must be separated by at least one; this allows to have adjacent ranges, like in RES_FRMATR_BEGIN, RES_FRMATR_END-1, RES_GRFATR_BEGIN, RES_GRFATR_END-1, where RES_FRMATR_END is equal to RES_GRFATR_BEGIN. Allowing this makes possible to (1) self-document the ranges, so it's clear which ranges are included; and (2) be safe in case when these constants would change, so that the one merged range would not unexpectedly contain everything inserted between RES_FRMATR_END and RES_GRFATR_BEGIN. Change-Id: Iaad0f099b85059b3aa318a347aa7fbd3f6d455c7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116909 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
126 lines
4.2 KiB
C++
126 lines
4.2 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <sal/config.h>
|
|
|
|
#include <sal/types.h>
|
|
#include <svl/itemset.hxx>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace svl::detail
|
|
{
|
|
/**
|
|
* Determines the number of sal_uInt16s in a 0-terminated array of pairs of
|
|
* sal_uInt16s, each representing a range of sal_uInt16s, and total capacity of the ranges.
|
|
* The terminating 0 is included in the count.
|
|
*/
|
|
inline std::pair<sal_uInt16, sal_uInt16> CountRanges(const sal_uInt16* pRanges)
|
|
{
|
|
sal_uInt16 nCount = 0;
|
|
sal_uInt16 nCapacity = 0;
|
|
if (pRanges)
|
|
{
|
|
nCount = 1;
|
|
while (*pRanges)
|
|
{
|
|
nCount += 2;
|
|
nCapacity += rangeSize(pRanges[0], pRanges[1]);
|
|
pRanges += 2;
|
|
}
|
|
}
|
|
return { nCount, nCapacity };
|
|
}
|
|
|
|
// Adds a range to which ranges, keeping the ranges in valid state (sorted, non-overlapping)
|
|
inline std::unique_ptr<sal_uInt16[]> MergeRange(const sal_uInt16* pWhichRanges, sal_uInt16 nFrom,
|
|
sal_uInt16 nTo)
|
|
{
|
|
assert(validRange(nFrom, nTo));
|
|
|
|
if (!pWhichRanges)
|
|
{
|
|
auto pNewRanges = std::make_unique<sal_uInt16[]>(3);
|
|
pNewRanges[0] = nFrom;
|
|
pNewRanges[1] = nTo;
|
|
pNewRanges[2] = 0;
|
|
return pNewRanges;
|
|
}
|
|
|
|
assert(validRanges(pWhichRanges));
|
|
|
|
// create vector of ranges (sal_uInt16 pairs of lower and upper bound)
|
|
const size_t nOldCount = CountRanges(pWhichRanges).first;
|
|
std::vector<std::pair<sal_uInt16, sal_uInt16>> aRangesTable;
|
|
aRangesTable.reserve(nOldCount / 2 + 1);
|
|
bool bAdded = false;
|
|
for (size_t i = 0; i + 1 < nOldCount; i += 2)
|
|
{
|
|
if (!bAdded && pWhichRanges[i] >= nFrom)
|
|
{ // insert new range, keep ranges sorted
|
|
aRangesTable.emplace_back(std::pair<sal_uInt16, sal_uInt16>(nFrom, nTo));
|
|
bAdded = true;
|
|
}
|
|
// insert current range
|
|
aRangesTable.emplace_back(
|
|
std::pair<sal_uInt16, sal_uInt16>(pWhichRanges[i], pWhichRanges[i + 1]));
|
|
}
|
|
if (!bAdded)
|
|
aRangesTable.emplace_back(std::pair<sal_uInt16, sal_uInt16>(nFrom, nTo));
|
|
|
|
// true if ranges overlap or adjoin, false if ranges are separate
|
|
auto needMerge
|
|
= [](std::pair<sal_uInt16, sal_uInt16> lhs, std::pair<sal_uInt16, sal_uInt16> rhs) {
|
|
return (lhs.first - 1) <= rhs.second && (rhs.first - 1) <= lhs.second;
|
|
};
|
|
|
|
auto it = aRangesTable.begin();
|
|
// we got at least one range
|
|
for (;;)
|
|
{
|
|
auto itNext = std::next(it);
|
|
if (itNext == aRangesTable.end())
|
|
break;
|
|
// check neighbouring ranges, find first range which overlaps or adjoins a previous range
|
|
if (needMerge(*it, *itNext))
|
|
{
|
|
// lower bounds are sorted, implies: it->first = min(it[0].first, it[1].first)
|
|
it->second = std::max(it->second, itNext->second);
|
|
aRangesTable.erase(itNext);
|
|
}
|
|
else
|
|
++it;
|
|
}
|
|
|
|
// construct range array
|
|
const size_t nNewSize = 2 * aRangesTable.size() + 1;
|
|
auto pNewRanges = std::make_unique<sal_uInt16[]>(nNewSize);
|
|
for (size_t i = 0; i < (nNewSize - 1); i += 2)
|
|
std::tie(pNewRanges[i], pNewRanges[i + 1]) = aRangesTable[i / 2];
|
|
|
|
pNewRanges[nNewSize - 1] = 0;
|
|
return pNewRanges;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|