/// <summary>Flush the state and update the header</summary> /// <returns>Slice contained the finished compressed bitmap</returns> /// <remarks>You cannot write any more words after Packing, until <see cref="Reset"/> is called.</remarks> public void Pack() { if (m_packed) { ThrowAlreadyPacked(); } // flush any pending word Flush(); if (m_words == 0) { // empty! m_bounds = BitRange.Empty; // there will be no header m_writer.Position = m_head; } else { // we need to find the lowest and highest bits m_bounds = CompressedBitmap.ComputeBounds(m_writer.ToMutableSlice(), m_words); // update the header int p; m_writer.Rewind(out p, m_head); //the last word is either a literal, or a 1-bit filler m_writer.WriteFixed32(CompressedWord.MakeHeader(m_bounds.Highest)); m_writer.Position = p; } m_packed = true; }
private void WriteFiller(uint word, int count) { Contract.Requires(count > 0); uint previous = m_current; if (previous == word) { // continuation of current run m_counter += count; return; } if (previous == NO_VALUE) { // start of new run m_counter += count; m_current = word; return; } // switch from one type to the other m_words += m_counter; if (previous == CompressedWord.ALL_ZEROES) { m_writer.WriteFixed32(CompressedWord.MakeZeroes(m_counter)); } else { m_writer.WriteFixed32(CompressedWord.MakeOnes(m_counter)); } m_counter = count; }
internal CompressedBitmapBuilder(CompressedWord[] words, int size, BitRange range) { Contract.Requires(words != null && size >= 0); m_words = words; m_size = size; m_lowest = range.Lowest; m_highest = range.Highest; }
private void SplitFiller(CompressedWord word, int offset, uint value, int relativeOffset) { bool set = word.FillBit == 1; int count = word.FillCount; // how many words we need to split // how many full words are there before our inserted literal? // in bits: index - position //Console.WriteLine("> Gap of " + (index - (position * 31)) + " in front of our literal"); int head = ((relativeOffset * 31) / 31); // how many empty words will stay before the inserted literal; int tail = count - head - 1; // how many empty words will stary after the inserted literal //Console.WriteLine("> Splitting 1-filler with repeat count {1} at {0}, with {2} before and {3} after", offset, count, head, tail); if (head > 0) { // keep the current filler, need to insert one or two words after it // update the current filler m_words[offset] = CompressedWord.MakeFiller(set, head); if (tail > 0) { // insert a literal and a filler //Console.WriteLine("> INSERT [...] ({0}:{1}) [{2}:0x{3:X8}] ({4}:{5}) [...]", offset, head, offset + 1, mask, offset + 2, tail); Shift(offset + 1, 2); m_words[offset + 1] = CompressedWord.MakeLiteral(value); m_words[offset + 2] = CompressedWord.MakeFiller(set, tail); } else { // only a literal //Console.WriteLine("> INSERT [...] ({0}:{1}) [0x{2:X8}] [...]", offset, head, mask); Shift(offset + 1, 1); m_words[offset + 1] = CompressedWord.MakeLiteral(value); } } else { if (tail > 0) { // replace current with a literal and add a filler //Console.WriteLine("> INSERT [....] [{0}:0x{1:X8}] ({2}:{3}) [...]", offset, mask, offset + 1, tail); Shift(offset + 1, 1); m_words[offset + 1] = CompressedWord.MakeFiller(set, tail); } else { // patch in place //Console.WriteLine("> PATCH [...] [{0}:0x{0:X8}] [...]", offset, mask); } m_words[offset] = CompressedWord.MakeLiteral(value); } }
internal static Slice Pack([NotNull] CompressedWord[] words, int size, int highest) { Contract.Requires(size >= 0 && size <= words.Length); if (size == 0) { // empty bitmap return(Slice.Empty); } var writer = new SliceWriter(checked ((size + 1) << 2)); writer.WriteFixed32(CompressedWord.MakeHeader(highest)); for (int i = 0; i < size; i++) { writer.WriteFixed32(words[i].RawValue); } return(writer.ToSlice()); }
/// <summary>Flush the curernt state of the writer</summary> /// <remarks>Writing after calling flush may break filler sequences into chunks, and reduce the efficiency of the compression. /// It should only be called if you need to split a bitmap streaming into physical chunks (sending on a socket, writing to disk, ...) /// </remarks> public void Flush() { if (m_packed) { ThrowAlreadyPacked(); } // either previous was a literal, or a run of zeroes or ones. Contract.Requires(m_counter == 0 ? (m_current == NO_VALUE) : (m_current == CompressedWord.ALL_ZEROES || m_current == CompressedWord.ALL_ONES)); int counter = m_counter; if (counter > 0 && m_current == CompressedWord.ALL_ONES) { // complete the last run only if it was all 1's m_writer.WriteFixed32(CompressedWord.MakeOnes(counter)); m_words += counter; } m_counter = 0; m_current = NO_VALUE; }
internal static CompressedWord[] DecodeWords(Slice data, int size, BitRange bounds) { Contract.Requires(size >= 0 && data.Count >= 4 && (data.Count & 3) == 0); int capacity = SliceHelpers.NextPowerOfTwo(size); if (capacity < 0) { capacity = size; } var words = new CompressedWord[capacity]; var end = data.Offset + data.Count; for (int i = 0, p = data.Offset + 4; p < end; i++, p += 4) { words[i] = new CompressedWord(data.ReadUInt32(p, 4)); } return(words); }
private void WriteLiteral(uint word, int count) { Contract.Requires(count > 0); uint previous = m_current; int counter = m_counter; // finish whatever was left open previously if (previous != NO_VALUE) { // need to close previous filler Contract.Assert(counter > 0); if (previous == CompressedWord.ALL_ZEROES) { m_writer.WriteFixed32(CompressedWord.MakeZeroes(counter)); } else if (previous == CompressedWord.ALL_ONES) { m_writer.WriteFixed32(CompressedWord.MakeOnes(counter)); } else { Contract.Assert(counter == 1); m_writer.WriteFixed32(CompressedWord.MakeLiteral(previous)); } m_words += counter; } // output the current literal int n = count; uint w = CompressedWord.MakeLiteral(word); while (n-- > 0) { m_writer.WriteFixed32(w); } m_words += count; m_current = NO_VALUE; m_counter = 0; }
/// <summary>Performs a logical NOT on a compressed bitmaps</summary> /// <param name="bitmap">Compressed bitmap</param> /// <param name="size">Minimum logical size of the result (bits in the uncompressed bitmap)</param> /// <returns>Compressed slice with the result of flipping all the bits in <paramref name="bitmap"/>, containing up to at least <paramref name="size"/> bits.</returns> /// <remarks>If <paramref name="bitmap"/> is larger than <paramref name="size"/>, then the resulting bitmap will be larger.</remarks> public static CompressedBitmap Not(this CompressedBitmap bitmap, int size) { if (bitmap == null) { throw new ArgumentNullException(nameof(bitmap)); } // there is a high change that the final bitmap will have the same size, with an optional extra filler word at the end var writer = new CompressedBitmapWriter(bitmap.Count + 1); int n = 0; if (bitmap.Count > 0) { foreach (var word in bitmap) { if (word.IsLiteral) { writer.Write(CompressedWord.MakeLiteral((uint)(~word.Literal))); n += 31; } else { int fc = word.FillCount; writer.Write(word.FillBit == 1 ? CompressedWord.ALL_ZEROES : CompressedWord.ALL_ONES, fc); n += 31 * fc; } } } if (n < size) { writer.Write(CompressedWord.ALL_ONES, size / 31); int r = size % 31; if (r > 0) { writer.Write((1u << r) - 1); } } return(writer.GetBitmap()); }
internal static CompressedWord[] DecodeWords(Slice data, int size, BitRange bounds) { Contract.Requires(size >= 0 && data.Count >= 4 && (data.Count & 3) == 0); int capacity = SliceHelpers.NextPowerOfTwo(size); if (capacity < 0) capacity = size; var words = new CompressedWord[capacity]; var end = data.Offset + data.Count; for (int i = 0, p = data.Offset + 4; p < end; i++, p += 4) { words[i] = new CompressedWord(data.ReadUInt32(p, 4)); } return words; }
/// <summary>Set a bit in the bitmap.</summary> /// <param name="index">Absolute index (0-based) of the bit to set</param> /// <returns>True if the bit was changed from 0 to 1; or false if it was already set.</returns> public bool Set(int index) { if (index < 0) throw new ArgumentException("Bit index cannot be less than zero.", "index"); //Console.WriteLine("Set({0}) on {1}-words bitmap", index, m_size); if (index > m_highest) m_highest = index; if (index < m_lowest) m_lowest = index; uint mask; int wordIndex = GetWordIndex(index, out mask); //Console.WriteLine("> bitOffset {0} is in data word #{1} with mask {2}", index, wordIndex, mask); int offset, position, count; if (!GetCompressedWordIndex(wordIndex, out offset, out position)) { // falls outside the bitmap, need to add new words count = wordIndex - position; if (count > 0) { //Console.WriteLine("> outside by {0}, need filler", count); EnsureCapacity(m_size + 2); m_words[m_size++] = CompressedWord.MakeZeroes(count); } else { //Console.WriteLine("> outside, right next to it"); EnsureCapacity(m_size + 1); } m_words[m_size++] = CompressedWord.MakeLiteral(mask); return true; } //Console.WriteLine("> would be in slot #{0} which starts at data-word #{1}", offset, position); // read the existing word var word = m_words[offset]; // we can patch literals in place if (word.IsLiteral) { //Console.WriteLine("> PATCH [...] [{0}:0x{0:X8}] [...]", offset, mask); var before = word.RawValue; var after = before | mask; if (before == after) return false; if (after == CompressedWord.ALL_ONES) { // convert to an all 1 literal! if (offset > 0) { // check if we can merge with a previous 1-filler var prev = m_words[offset - 1]; if (!prev.IsLiteral && prev.FillBit == 1) { m_words[offset - 1] = CompressedWord.MakeOnes(prev.FillCount + 1); int r = m_size - offset - 1; if (r > 0) Array.Copy(m_words, offset + 1, m_words, offset, r); --m_size; return true; } } //TODO: also need to check if the next one is also a filler! // convert this one to a filler after = CompressedWord.MakeOnes(1); } m_words[offset] = new CompressedWord(after); return true; } // if it an all-1 filler, our job is already done if (word.FillBit == 1) { //Console.WriteLine("> was already a 1-filler"); return false; } // for all-1 fillers, we must break them so that we can insert a new literal SplitFiller(word, offset, mask, wordIndex - position); return true; }
public bool Equals(CompressedWord word) { return(this.RawValue == word.RawValue); }
public bool Equals(CompressedWord word) { return this.RawValue == word.RawValue; }
/// <summary>Clear a bit in the bitmap</summary> /// <param name="index">Offset (0-based) of the bit to clear</param> /// <returns>True if the bit was changed from 1 to 0; or false if it was already unset.</returns> public bool Clear(int index) { if (index < 0) { throw new ArgumentException("Bit index cannot be less than zero.", "index"); } uint mask; int wordIndex = GetWordIndex(index, out mask); int offset, position; if (!GetCompressedWordIndex(wordIndex, out offset, out position)) { // outside the buffer, nothing to do return(false); } var word = m_words[offset]; if (!word.IsLiteral) { if (word.FillBit == 0) { // already a 0, nothing to do return(false); } // for all-1 fillers, we must break them so that we can insert a new literal SplitFiller(word, offset, ~mask, wordIndex - position); //TODO: update lowest/highest? return(true); } // patch the literal uint o = word.RawValue; uint w = o & ~mask; if (w == o) { // no changes return(false); } if (w == 0) { // last bit was removed if (offset == m_size - 1) { // that was the last one, truncate! // also kill any 0-fillers up until there --m_size; while (m_size > 0) { int p = m_size - 1; if (m_words[p].FillBit != 0 || m_words[p].IsLiteral) { break; } m_size = p; } //TODO: recompute lowest/highest! } else { // convert to filler m_words[offset] = CompressedWord.MakeZeroes(1); //TODO: merge! } } else { m_words[offset] = CompressedWord.MakeLiteral(w); } //TODO: update lowest/higest! return(true); }
/// <summary>Set a bit in the bitmap.</summary> /// <param name="index">Absolute index (0-based) of the bit to set</param> /// <returns>True if the bit was changed from 0 to 1; or false if it was already set.</returns> public bool Set(int index) { if (index < 0) { throw new ArgumentException("Bit index cannot be less than zero.", "index"); } //Console.WriteLine("Set({0}) on {1}-words bitmap", index, m_size); if (index > m_highest) { m_highest = index; } if (index < m_lowest) { m_lowest = index; } uint mask; int wordIndex = GetWordIndex(index, out mask); //Console.WriteLine("> bitOffset {0} is in data word #{1} with mask {2}", index, wordIndex, mask); int offset, position, count; if (!GetCompressedWordIndex(wordIndex, out offset, out position)) { // falls outside the bitmap, need to add new words count = wordIndex - position; if (count > 0) { //Console.WriteLine("> outside by {0}, need filler", count); EnsureCapacity(m_size + 2); m_words[m_size++] = CompressedWord.MakeZeroes(count); } else { //Console.WriteLine("> outside, right next to it"); EnsureCapacity(m_size + 1); } m_words[m_size++] = CompressedWord.MakeLiteral(mask); return(true); } //Console.WriteLine("> would be in slot #{0} which starts at data-word #{1}", offset, position); // read the existing word var word = m_words[offset]; // we can patch literals in place if (word.IsLiteral) { //Console.WriteLine("> PATCH [...] [{0}:0x{0:X8}] [...]", offset, mask); var before = word.RawValue; var after = before | mask; if (before == after) { return(false); } if (after == CompressedWord.ALL_ONES) { // convert to an all 1 literal! if (offset > 0) { // check if we can merge with a previous 1-filler var prev = m_words[offset - 1]; if (!prev.IsLiteral && prev.FillBit == 1) { m_words[offset - 1] = CompressedWord.MakeOnes(prev.FillCount + 1); int r = m_size - offset - 1; if (r > 0) { Array.Copy(m_words, offset + 1, m_words, offset, r); } --m_size; return(true); } } //TODO: also need to check if the next one is also a filler! // convert this one to a filler after = CompressedWord.MakeOnes(1); } m_words[offset] = new CompressedWord(after); return(true); } // if it an all-1 filler, our job is already done if (word.FillBit == 1) { //Console.WriteLine("> was already a 1-filler"); return(false); } // for all-1 fillers, we must break them so that we can insert a new literal SplitFiller(word, offset, mask, wordIndex - position); return(true); }