summaryrefslogtreecommitdiffstats
path: root/toolkit/components/url-classifier/ChunkSet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/url-classifier/ChunkSet.cpp')
-rw-r--r--toolkit/components/url-classifier/ChunkSet.cpp248
1 files changed, 248 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/ChunkSet.cpp b/toolkit/components/url-classifier/ChunkSet.cpp
new file mode 100644
index 0000000000..af488d43d7
--- /dev/null
+++ b/toolkit/components/url-classifier/ChunkSet.cpp
@@ -0,0 +1,248 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "ChunkSet.h"
+
+#include "nsReadableUtils.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+const size_t ChunkSet::IO_BUFFER_SIZE;
+
+nsresult ChunkSet::Serialize(nsACString& aChunkStr) {
+ // Truncate and append rather than assigning because that's more efficient if
+ // aString is an nsAutoCString.
+ aChunkStr.Truncate();
+ StringJoinAppend(aChunkStr, ","_ns, mRanges,
+ [](nsACString& dst, const Range& range) {
+ dst.AppendInt((int32_t)range.Begin());
+ if (range.Begin() != range.End()) {
+ dst.Append('-');
+ dst.AppendInt((int32_t)range.End());
+ }
+ });
+
+ return NS_OK;
+}
+
+nsresult ChunkSet::Set(uint32_t aChunk) {
+ if (!Has(aChunk)) {
+ Range chunkRange(aChunk, aChunk);
+
+ if (mRanges.Length() == 0) {
+ if (!mRanges.AppendElement(chunkRange, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+ }
+
+ if (mRanges.LastElement().Precedes(chunkRange)) {
+ mRanges.LastElement().End(aChunk);
+ } else if (chunkRange.Precedes(mRanges[0])) {
+ mRanges[0].Begin(aChunk);
+ } else {
+ ChunkSet tmp;
+ if (!tmp.mRanges.AppendElement(chunkRange, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return Merge(tmp);
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ChunkSet::Has(uint32_t aChunk) const {
+ size_t idx;
+ return BinarySearchIf(mRanges, 0, mRanges.Length(),
+ Range::IntersectionComparator(Range(aChunk, aChunk)),
+ &idx);
+ // IntersectionComparator works because we create a
+ // single-chunk range.
+}
+
+nsresult ChunkSet::Merge(const ChunkSet& aOther) {
+ size_t oldLen = mRanges.Length();
+
+ for (const Range& mergeRange : aOther.mRanges) {
+ if (!HasSubrange(mergeRange)) {
+ if (!mRanges.InsertElementSorted(mergeRange, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ if (oldLen < mRanges.Length()) {
+ for (size_t i = 1; i < mRanges.Length(); i++) {
+ while (mRanges[i - 1].FoldLeft(mRanges[i])) {
+ mRanges.RemoveElementAt(i);
+
+ if (i == mRanges.Length()) {
+ return NS_OK;
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+uint32_t ChunkSet::Length() const {
+ uint32_t len = 0;
+ for (const Range& range : mRanges) {
+ len += range.Length();
+ }
+
+ return len;
+}
+
+nsresult ChunkSet::Remove(const ChunkSet& aOther) {
+ for (const Range& removalRange : aOther.mRanges) {
+ if (mRanges.Length() == 0) {
+ return NS_OK;
+ }
+
+ if (mRanges.LastElement().End() < removalRange.Begin() ||
+ aOther.mRanges.LastElement().End() < mRanges[0].Begin()) {
+ return NS_OK;
+ }
+
+ size_t intersectionIdx;
+ while (BinarySearchIf(mRanges, 0, mRanges.Length(),
+ Range::IntersectionComparator(removalRange),
+ &intersectionIdx)) {
+ ChunkSet remains;
+ nsresult rv = mRanges[intersectionIdx].Remove(removalRange, remains);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mRanges.RemoveElementAt(intersectionIdx);
+ if (!mRanges.InsertElementsAt(intersectionIdx, remains.mRanges,
+ fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+void ChunkSet::Clear() { mRanges.Clear(); }
+
+nsresult ChunkSet::Write(nsIOutputStream* aOut) const {
+ nsTArray<uint32_t> chunks(IO_BUFFER_SIZE);
+
+ for (const Range& range : mRanges) {
+ for (uint32_t chunk = range.Begin(); chunk <= range.End(); chunk++) {
+ chunks.AppendElement(chunk);
+
+ if (chunks.Length() == chunks.Capacity()) {
+ nsresult rv = WriteTArray(aOut, chunks);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ chunks.Clear();
+ }
+ }
+ }
+
+ nsresult rv = WriteTArray(aOut, chunks);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult ChunkSet::Read(nsIInputStream* aIn, uint32_t aNumElements) {
+ nsTArray<uint32_t> chunks(IO_BUFFER_SIZE);
+
+ while (aNumElements != 0) {
+ chunks.Clear();
+
+ uint32_t numToRead =
+ aNumElements > IO_BUFFER_SIZE ? IO_BUFFER_SIZE : aNumElements;
+
+ nsresult rv = ReadTArray(aIn, &chunks, numToRead);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ aNumElements -= numToRead;
+
+ for (uint32_t c : chunks) {
+ rv = Set(c);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ChunkSet::HasSubrange(const Range& aSubrange) const {
+ for (const Range& range : mRanges) {
+ if (range.Contains(aSubrange)) {
+ return true;
+ } else if (!(aSubrange.Begin() > range.End() ||
+ range.Begin() > aSubrange.End())) {
+ // In this case, aSubrange overlaps this range but is not a subrange.
+ // because the ChunkSet implementation ensures that there are no
+ // overlapping ranges, this means that aSubrange cannot be a subrange of
+ // any of the following ranges
+ return false;
+ }
+ }
+
+ return false;
+}
+
+uint32_t ChunkSet::Range::Length() const { return mEnd - mBegin + 1; }
+
+nsresult ChunkSet::Range::Remove(const Range& aRange,
+ ChunkSet& aRemainderSet) const {
+ if (mBegin < aRange.mBegin && aRange.mBegin <= mEnd) {
+ // aRange overlaps & follows this range
+ Range range(mBegin, aRange.mBegin - 1);
+ if (!aRemainderSet.mRanges.AppendElement(range, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ if (mBegin <= aRange.mEnd && aRange.mEnd < mEnd) {
+ // aRange overlaps & precedes this range
+ Range range(aRange.mEnd + 1, mEnd);
+ if (!aRemainderSet.mRanges.AppendElement(range, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ChunkSet::Range::FoldLeft(const Range& aRange) {
+ if (Contains(aRange)) {
+ return true;
+ } else if (Precedes(aRange) ||
+ (mBegin <= aRange.mBegin && aRange.mBegin <= mEnd)) {
+ mEnd = aRange.mEnd;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace safebrowsing
+} // namespace mozilla