| 1 | /* |
| 2 | * Copyright (C) 2017 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| 17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 23 | * THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "MarkedText.h" |
| 28 | |
| 29 | #include <algorithm> |
| 30 | #include <wtf/HashSet.h> |
| 31 | |
| 32 | namespace WebCore { |
| 33 | |
| 34 | Vector<MarkedText> subdivide(const Vector<MarkedText>& markedTexts, OverlapStrategy overlapStrategy) |
| 35 | { |
| 36 | if (markedTexts.isEmpty()) |
| 37 | return { }; |
| 38 | |
| 39 | struct Offset { |
| 40 | enum Kind { Begin, End }; |
| 41 | Kind kind; |
| 42 | unsigned value; // Copy of markedText.startOffset/endOffset to avoid the need to branch based on kind. |
| 43 | const MarkedText* markedText; |
| 44 | }; |
| 45 | |
| 46 | // 1. Build table of all offsets. |
| 47 | Vector<Offset> offsets; |
| 48 | ASSERT(markedTexts.size() < std::numeric_limits<unsigned>::max() / 2); |
| 49 | unsigned numberOfMarkedTexts = markedTexts.size(); |
| 50 | unsigned numberOfOffsets = 2 * numberOfMarkedTexts; |
| 51 | offsets.reserveInitialCapacity(numberOfOffsets); |
| 52 | for (auto& markedText : markedTexts) { |
| 53 | offsets.uncheckedAppend({ Offset::Begin, markedText.startOffset, &markedText }); |
| 54 | offsets.uncheckedAppend({ Offset::End, markedText.endOffset, &markedText }); |
| 55 | } |
| 56 | |
| 57 | // 2. Sort offsets such that begin offsets are in paint order and end offsets are in reverse paint order. |
| 58 | std::sort(offsets.begin(), offsets.end(), [] (const Offset& a, const Offset& b) { |
| 59 | return a.value < b.value || (a.value == b.value && a.kind == b.kind && a.kind == Offset::Begin && a.markedText->type < b.markedText->type) |
| 60 | || (a.value == b.value && a.kind == b.kind && a.kind == Offset::End && a.markedText->type > b.markedText->type); |
| 61 | }); |
| 62 | |
| 63 | // 3. Compute intersection. |
| 64 | Vector<MarkedText> result; |
| 65 | result.reserveInitialCapacity(numberOfMarkedTexts); |
| 66 | HashSet<const MarkedText*> processedMarkedTexts; |
| 67 | unsigned offsetSoFar = offsets[0].value; |
| 68 | for (unsigned i = 1; i < numberOfOffsets; ++i) { |
| 69 | if (offsets[i].value > offsets[i - 1].value) { |
| 70 | if (overlapStrategy == OverlapStrategy::Frontmost) { |
| 71 | Optional<unsigned> frontmost; |
| 72 | for (unsigned j = 0; j < i; ++j) { |
| 73 | if (!processedMarkedTexts.contains(offsets[j].markedText) && (!frontmost || offsets[j].markedText->type > offsets[*frontmost].markedText->type)) |
| 74 | frontmost = j; |
| 75 | } |
| 76 | if (frontmost) |
| 77 | result.append({ offsetSoFar, offsets[i].value, offsets[*frontmost].markedText->type, offsets[*frontmost].markedText->marker }); |
| 78 | } else { |
| 79 | // The appended marked texts may not be in paint order. We will fix this up at the end of this function. |
| 80 | for (unsigned j = 0; j < i; ++j) { |
| 81 | if (!processedMarkedTexts.contains(offsets[j].markedText)) |
| 82 | result.append({ offsetSoFar, offsets[i].value, offsets[j].markedText->type, offsets[j].markedText->marker }); |
| 83 | } |
| 84 | } |
| 85 | offsetSoFar = offsets[i].value; |
| 86 | } |
| 87 | if (offsets[i].kind == Offset::End) |
| 88 | processedMarkedTexts.add(offsets[i].markedText); |
| 89 | } |
| 90 | // Fix up; sort the marked texts so that they are in paint order. |
| 91 | if (overlapStrategy == OverlapStrategy::None) |
| 92 | std::sort(result.begin(), result.end(), [] (const MarkedText& a, const MarkedText& b) { return a.startOffset < b.startOffset || (a.startOffset == b.startOffset && a.type < b.type); }); |
| 93 | return result; |
| 94 | } |
| 95 | |
| 96 | } |
| 97 | |
| 98 | |
| 99 | |