/// <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;
		}
예제 #4
0
        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);
            }
        }
예제 #5
0
        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;
        }
예제 #7
0
        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;
        }
예제 #9
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;
		}
		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);
			}
		}
		/// <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;
		}
예제 #13
0
 public bool Equals(CompressedWord word)
 {
     return(this.RawValue == word.RawValue);
 }
		public bool Equals(CompressedWord word)
		{
			return this.RawValue == word.RawValue;
		}
예제 #15
0
        /// <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);
        }
예제 #16
0
        /// <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);
        }