1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "gfxSkipChars.h"
7 #include "mozilla/BinarySearch.h"
8 #include "mozilla/gfx/Logging.h"
9 
10 struct SkippedRangeStartComparator {
11   const uint32_t mOffset;
SkippedRangeStartComparatorSkippedRangeStartComparator12   explicit SkippedRangeStartComparator(const uint32_t aOffset)
13       : mOffset(aOffset) {}
operator ()SkippedRangeStartComparator14   int operator()(const gfxSkipChars::SkippedRange& aRange) const {
15     return (mOffset < aRange.Start()) ? -1 : 1;
16   }
17 };
18 
SetOriginalOffset(int32_t aOffset)19 void gfxSkipCharsIterator::SetOriginalOffset(int32_t aOffset) {
20   aOffset += mOriginalStringToSkipCharsOffset;
21   if (MOZ_UNLIKELY(uint32_t(aOffset) > mSkipChars->mCharCount)) {
22     gfxCriticalError() << "invalid offset " << aOffset
23                        << " for gfxSkipChars length " << mSkipChars->mCharCount;
24     aOffset = mSkipChars->mCharCount;
25   }
26 
27   mOriginalStringOffset = aOffset;
28 
29   const uint32_t rangeCount = mSkipChars->mRanges.Length();
30   if (rangeCount == 0) {
31     mSkippedStringOffset = aOffset;
32     return;
33   }
34 
35   // at start of string?
36   if (aOffset == 0) {
37     mSkippedStringOffset = 0;
38     mCurrentRangeIndex =
39         rangeCount && mSkipChars->mRanges[0].Start() == 0 ? 0 : -1;
40     return;
41   }
42 
43   // find the range that includes or precedes aOffset
44   const nsTArray<gfxSkipChars::SkippedRange>& ranges = mSkipChars->mRanges;
45   size_t idx;
46   mozilla::BinarySearchIf(ranges, 0, rangeCount,
47                           SkippedRangeStartComparator(aOffset), &idx);
48 
49   if (idx == rangeCount) {
50     mCurrentRangeIndex = rangeCount - 1;
51   } else if (uint32_t(aOffset) < ranges[idx].Start()) {
52     mCurrentRangeIndex = idx - 1;
53     if (mCurrentRangeIndex == -1) {
54       mSkippedStringOffset = aOffset;
55       return;
56     }
57   } else {
58     mCurrentRangeIndex = idx;
59   }
60 
61   const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex];
62   if (uint32_t(aOffset) < r.End()) {
63     mSkippedStringOffset = r.SkippedOffset();
64     return;
65   }
66 
67   mSkippedStringOffset = aOffset - r.NextDelta();
68 }
69 
70 struct SkippedRangeOffsetComparator {
71   const uint32_t mOffset;
SkippedRangeOffsetComparatorSkippedRangeOffsetComparator72   explicit SkippedRangeOffsetComparator(const uint32_t aOffset)
73       : mOffset(aOffset) {}
operator ()SkippedRangeOffsetComparator74   int operator()(const gfxSkipChars::SkippedRange& aRange) const {
75     return (mOffset < aRange.SkippedOffset()) ? -1 : 1;
76   }
77 };
78 
SetSkippedOffset(uint32_t aOffset)79 void gfxSkipCharsIterator::SetSkippedOffset(uint32_t aOffset) {
80   NS_ASSERTION(
81       (mSkipChars->mRanges.IsEmpty() && aOffset <= mSkipChars->mCharCount) ||
82           (aOffset <= mSkipChars->LastRange().SkippedOffset() +
83                           mSkipChars->mCharCount -
84                           mSkipChars->LastRange().End()),
85       "Invalid skipped offset");
86   mSkippedStringOffset = aOffset;
87 
88   uint32_t rangeCount = mSkipChars->mRanges.Length();
89   if (rangeCount == 0) {
90     mOriginalStringOffset = aOffset;
91     return;
92   }
93 
94   const nsTArray<gfxSkipChars::SkippedRange>& ranges = mSkipChars->mRanges;
95   size_t idx;
96   mozilla::BinarySearchIf(ranges, 0, rangeCount,
97                           SkippedRangeOffsetComparator(aOffset), &idx);
98 
99   if (idx == rangeCount) {
100     mCurrentRangeIndex = rangeCount - 1;
101   } else if (aOffset < ranges[idx].SkippedOffset()) {
102     mCurrentRangeIndex = idx - 1;
103     if (mCurrentRangeIndex == -1) {
104       mOriginalStringOffset = aOffset;
105       return;
106     }
107   } else {
108     mCurrentRangeIndex = idx;
109   }
110 
111   const gfxSkipChars::SkippedRange& r = ranges[mCurrentRangeIndex];
112   mOriginalStringOffset = r.End() + aOffset - r.SkippedOffset();
113 }
114 
IsOriginalCharSkipped(int32_t * aRunLength) const115 bool gfxSkipCharsIterator::IsOriginalCharSkipped(int32_t* aRunLength) const {
116   if (mCurrentRangeIndex == -1) {
117     // we're before the first skipped range (if any)
118     if (aRunLength) {
119       uint32_t end = mSkipChars->mRanges.IsEmpty()
120                          ? mSkipChars->mCharCount
121                          : mSkipChars->mRanges[0].Start();
122       *aRunLength = end - mOriginalStringOffset;
123     }
124     return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset);
125   }
126 
127   const gfxSkipChars::SkippedRange& range =
128       mSkipChars->mRanges[mCurrentRangeIndex];
129 
130   if (uint32_t(mOriginalStringOffset) < range.End()) {
131     if (aRunLength) {
132       *aRunLength = range.End() - mOriginalStringOffset;
133     }
134     return true;
135   }
136 
137   if (aRunLength) {
138     uint32_t end =
139         uint32_t(mCurrentRangeIndex) + 1 < mSkipChars->mRanges.Length()
140             ? mSkipChars->mRanges[mCurrentRangeIndex + 1].Start()
141             : mSkipChars->mCharCount;
142     *aRunLength = end - mOriginalStringOffset;
143   }
144 
145   return mSkipChars->mCharCount == uint32_t(mOriginalStringOffset);
146 }
147