Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/url-classifier/ChunkSet.cpp
Line
Count
Source (jump to first uncovered line)
1
//* -*- Mode: C++; tab-width: 8; 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 "ChunkSet.h"
7
8
namespace mozilla {
9
namespace safebrowsing {
10
11
const size_t ChunkSet::IO_BUFFER_SIZE;
12
13
nsresult
14
ChunkSet::Serialize(nsACString& aChunkStr)
15
0
{
16
0
  aChunkStr.Truncate();
17
0
  for (const Range& range : mRanges) {
18
0
    if (&range != &mRanges[0]) {
19
0
      aChunkStr.Append(',');
20
0
    }
21
0
22
0
    aChunkStr.AppendInt((int32_t)range.Begin());
23
0
    if (range.Begin() != range.End()) {
24
0
      aChunkStr.Append('-');
25
0
      aChunkStr.AppendInt((int32_t)range.End());
26
0
    }
27
0
  }
28
0
29
0
  return NS_OK;
30
0
}
31
32
nsresult
33
ChunkSet::Set(uint32_t aChunk)
34
0
{
35
0
  if (!Has(aChunk)) {
36
0
    Range chunkRange(aChunk, aChunk);
37
0
38
0
    if (mRanges.Length() == 0) {
39
0
      if (!mRanges.AppendElement(chunkRange, fallible)) {
40
0
        return NS_ERROR_OUT_OF_MEMORY;
41
0
      }
42
0
      return NS_OK;
43
0
    }
44
0
45
0
    if (mRanges.LastElement().Precedes(chunkRange)) {
46
0
      mRanges.LastElement().End(aChunk);
47
0
    } else if (chunkRange.Precedes(mRanges[0])) {
48
0
      mRanges[0].Begin(aChunk);
49
0
    } else {
50
0
      ChunkSet tmp;
51
0
      if (!tmp.mRanges.AppendElement(chunkRange, fallible)) {
52
0
        return NS_ERROR_OUT_OF_MEMORY;
53
0
      }
54
0
55
0
      return Merge(tmp);
56
0
    }
57
0
  }
58
0
59
0
  return NS_OK;
60
0
}
61
62
bool
63
ChunkSet::Has(uint32_t aChunk) const
64
0
{
65
0
  size_t idx;
66
0
  return BinarySearchIf(mRanges, 0, mRanges.Length(),
67
0
                        Range::IntersectionComparator(Range(aChunk, aChunk)),
68
0
                        &idx);
69
0
                        // IntersectionComparator works because we create a
70
0
                        // single-chunk range.
71
0
}
72
73
nsresult
74
ChunkSet::Merge(const ChunkSet& aOther)
75
0
{
76
0
  size_t oldLen = mRanges.Length();
77
0
78
0
  for (const Range& mergeRange : aOther.mRanges) {
79
0
    if (!HasSubrange(mergeRange)) {
80
0
      if (!mRanges.InsertElementSorted(mergeRange, fallible)) {
81
0
        return NS_ERROR_OUT_OF_MEMORY;
82
0
      }
83
0
    }
84
0
  }
85
0
86
0
  if (oldLen < mRanges.Length()) {
87
0
    for (size_t i = 1; i < mRanges.Length(); i++) {
88
0
      while (mRanges[i - 1].FoldLeft(mRanges[i])) {
89
0
        mRanges.RemoveElementAt(i);
90
0
91
0
        if (i == mRanges.Length()) {
92
0
          return NS_OK;
93
0
        }
94
0
      }
95
0
    }
96
0
  }
97
0
98
0
  return NS_OK;
99
0
}
100
101
uint32_t
102
ChunkSet::Length() const
103
0
{
104
0
  uint32_t len = 0;
105
0
  for (const Range& range : mRanges) {
106
0
    len += range.Length();
107
0
  }
108
0
109
0
  return len;
110
0
}
111
112
nsresult
113
ChunkSet::Remove(const ChunkSet& aOther)
114
0
{
115
0
  for (const Range& removalRange : aOther.mRanges) {
116
0
117
0
    if (mRanges.Length() == 0) {
118
0
      return NS_OK;
119
0
    }
120
0
121
0
    if (mRanges.LastElement().End() < removalRange.Begin() ||
122
0
        aOther.mRanges.LastElement().End() < mRanges[0].Begin()) {
123
0
      return NS_OK;
124
0
    }
125
0
126
0
    size_t intersectionIdx;
127
0
    while (BinarySearchIf(mRanges, 0, mRanges.Length(),
128
0
           Range::IntersectionComparator(removalRange), &intersectionIdx)) {
129
0
130
0
      ChunkSet remains;
131
0
      nsresult rv = mRanges[intersectionIdx].Remove(removalRange, remains);
132
0
133
0
      if (NS_FAILED(rv)) {
134
0
        return rv;
135
0
      }
136
0
137
0
      mRanges.RemoveElementAt(intersectionIdx);
138
0
      if (!mRanges.InsertElementsAt(intersectionIdx, remains.mRanges,
139
0
                                    fallible)) {
140
0
        return NS_ERROR_OUT_OF_MEMORY;
141
0
      }
142
0
    }
143
0
  }
144
0
145
0
  return NS_OK;
146
0
}
147
148
void
149
ChunkSet::Clear()
150
0
{
151
0
  mRanges.Clear();
152
0
}
153
154
nsresult
155
ChunkSet::Write(nsIOutputStream* aOut) const
156
0
{
157
0
  nsTArray<uint32_t> chunks(IO_BUFFER_SIZE);
158
0
159
0
  for (const Range& range : mRanges) {
160
0
    for (uint32_t chunk = range.Begin(); chunk <= range.End(); chunk++) {
161
0
      chunks.AppendElement(chunk);
162
0
163
0
      if (chunks.Length() == chunks.Capacity()) {
164
0
        nsresult rv = WriteTArray(aOut, chunks);
165
0
166
0
        if (NS_FAILED(rv)) {
167
0
          return rv;
168
0
        }
169
0
170
0
        chunks.Clear();
171
0
      }
172
0
    }
173
0
  }
174
0
175
0
  nsresult rv = WriteTArray(aOut, chunks);
176
0
177
0
  if (NS_FAILED(rv)) {
178
0
    return rv;
179
0
  }
180
0
181
0
  return NS_OK;
182
0
}
183
184
nsresult
185
ChunkSet::Read(nsIInputStream* aIn, uint32_t aNumElements)
186
0
{
187
0
  nsTArray<uint32_t> chunks(IO_BUFFER_SIZE);
188
0
189
0
  while (aNumElements != 0) {
190
0
    chunks.Clear();
191
0
192
0
    uint32_t numToRead = aNumElements > IO_BUFFER_SIZE ? IO_BUFFER_SIZE : aNumElements;
193
0
194
0
    nsresult rv = ReadTArray(aIn, &chunks, numToRead);
195
0
196
0
    if (NS_FAILED(rv)) {
197
0
      return rv;
198
0
    }
199
0
200
0
    aNumElements -= numToRead;
201
0
202
0
    for (uint32_t c : chunks) {
203
0
      rv = Set(c);
204
0
205
0
      if (NS_FAILED(rv)) {
206
0
        return rv;
207
0
      }
208
0
    }
209
0
  }
210
0
211
0
  return NS_OK;
212
0
}
213
214
bool
215
ChunkSet::HasSubrange(const Range& aSubrange) const
216
0
{
217
0
  for (const Range& range : mRanges) {
218
0
    if (range.Contains(aSubrange)) {
219
0
      return true;
220
0
    } else if (!(aSubrange.Begin() > range.End() ||
221
0
                 range.Begin() > aSubrange.End())) {
222
0
      // In this case, aSubrange overlaps this range but is not a subrange.
223
0
      // because the ChunkSet implementation ensures that there are no
224
0
      // overlapping ranges, this means that aSubrange cannot be a subrange of
225
0
      // any of the following ranges
226
0
      return false;
227
0
    }
228
0
  }
229
0
230
0
  return false;
231
0
}
232
233
uint32_t
234
ChunkSet::Range::Length() const
235
0
{
236
0
  return mEnd - mBegin + 1;
237
0
}
238
239
nsresult
240
ChunkSet::Range::Remove(const Range& aRange, ChunkSet& aRemainderSet) const
241
0
{
242
0
  if (mBegin < aRange.mBegin && aRange.mBegin <= mEnd) {
243
0
    // aRange overlaps & follows this range
244
0
    Range range(mBegin, aRange.mBegin - 1);
245
0
    if (!aRemainderSet.mRanges.AppendElement(range, fallible)) {
246
0
      return NS_ERROR_OUT_OF_MEMORY;
247
0
    }
248
0
  }
249
0
250
0
  if (mBegin <= aRange.mEnd && aRange.mEnd < mEnd) {
251
0
    // aRange overlaps & precedes this range
252
0
    Range range(aRange.mEnd + 1, mEnd);
253
0
    if (!aRemainderSet.mRanges.AppendElement(range, fallible)) {
254
0
      return NS_ERROR_OUT_OF_MEMORY;
255
0
    }
256
0
  }
257
0
258
0
  return NS_OK;
259
0
}
260
261
bool
262
ChunkSet::Range::FoldLeft(const Range& aRange)
263
0
{
264
0
  if (Contains(aRange)) {
265
0
    return true;
266
0
  } else if (Precedes(aRange) ||
267
0
             (mBegin <= aRange.mBegin && aRange.mBegin <= mEnd)) {
268
0
    mEnd = aRange.mEnd;
269
0
    return true;
270
0
  }
271
0
272
0
  return false;
273
0
}
274
275
} // namespace safebrowsing
276
} // namespace mozilla