mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-21 12:23:44 +00:00
library/bimap.m:
library/bitmap.m:
library/calendar.m:
library/char.m:
library/cord.m:
library/deconstruct.m:
library/diet.m:
library/dir.m:
library/eqvclass.m:
library/map.m:
library/pprint.m:
library/pqueue.m:
library/stream.string_writer.m:
library/term_conversion.m:
library/time.m:
library/type_desc.m:
library/version_array.m:
library/version_array2d.m:
library/version_bitmap.m:
library/version_hash_table.m:
Fix inconsistencies between (a) the order in which functions and predicates
are declared, and (b) the order in which they are defined.
In most of these modules, either the order of the declarations
or the order of the definitions made sense, and I changed the other
to match. In a few modules, neither made sense, so I changed *both*
to an order that *does* make sense (i.e. it has related predicates
together).
In some places, put dividers between groups of related
functions/predicates, to make the groups themselves more visible.
In some places, fix comments or programming style, give some auxiliary
(non-exported) predicates better names, or delete some unneeded module
qualifications.
In some places, have the function form of a procedure forward
the work to the predicate form, instead of vice versa, where this is
more natural (usually because it allows the use of state variables).
However, this is the only kind of "algorithmic" change in this diff;
the rest is just moving code around.
2053 lines
69 KiB
Mathematica
2053 lines
69 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2001-2002, 2004-2007, 2009-2011 The University of Melbourne
|
|
% This file may only be copied under the terms of the GNU Library General
|
|
% Public License - see the file COPYING.LIB in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: bitmap.m.
|
|
% Main author: rafe, stayl.
|
|
% Stability: low.
|
|
%
|
|
% Efficient bitmap implementation.
|
|
%
|
|
% CAVEAT: the user is referred to the documentation in the header
|
|
% of array.m regarding programming with unique objects (the compiler
|
|
% does not currently recognise them, hence we are forced to use
|
|
% non-unique modes until the situation is rectified; this places
|
|
% a small burden on the programmer to ensure the correctness of his
|
|
% code that would otherwise be assured by the compiler.)
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module bitmap.
|
|
:- interface.
|
|
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Type `bitmap' is equivalent to `array(bool)', but is implemented
|
|
% much more efficiently. Accessing bitmaps as if they were an array
|
|
% of eight bit bytes is especially efficient.
|
|
%
|
|
% See runtime/mercury_types.h for the definition of MR_BitmapPtr for
|
|
% use in foreign code.
|
|
%
|
|
% Comparison of bitmaps first compares the size, if the size is equal
|
|
% then each bit in turn is compared starting from bit zero.
|
|
%
|
|
:- type bitmap.
|
|
|
|
:- inst bitmap == ground.
|
|
:- inst uniq_bitmap == bitmap. % XXX should be unique
|
|
:- mode bitmap_di == in(uniq_bitmap). % XXX should be di
|
|
:- mode bitmap_uo == out(uniq_bitmap).
|
|
:- mode bitmap_ui == in(uniq_bitmap).
|
|
|
|
% The exception thrown for any error.
|
|
%
|
|
:- type bitmap_error
|
|
---> bitmap_error(string).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type bit_index == int.
|
|
:- type byte_index == int.
|
|
:- type num_bits == int.
|
|
:- type num_bytes == int.
|
|
|
|
% 8 bits stored in the least significant bits of the integer.
|
|
%
|
|
:- type byte == int.
|
|
|
|
% An integer interpreted as a vector of int.bits_per_int bits.
|
|
%
|
|
:- type word == int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% init(N, B) creates a bitmap of size N (indexed 0 .. N-1)
|
|
% setting each bit if B = yes and clearing each bit if B = no.
|
|
% An exception is thrown if N is negative.
|
|
%
|
|
:- func init(num_bits::in, bool::in) = (bitmap::bitmap_uo) is det.
|
|
|
|
% A synonym for init(N, no).
|
|
%
|
|
:- func init(num_bits::in) = (bitmap::bitmap_uo) is det.
|
|
|
|
% Is the given bit number in range?
|
|
%
|
|
:- pred in_range(bitmap, bit_index).
|
|
% :- mode in_range(bitmap_ui, in) is semidet.
|
|
:- mode in_range(in, in) is semidet.
|
|
|
|
% Is the given byte number in range?
|
|
%
|
|
:- pred byte_in_range(bitmap, byte_index).
|
|
% :- mode byte_in_range(bitmap_ui, in) is semidet.
|
|
:- mode byte_in_range(in, in) is semidet.
|
|
|
|
% Return the number of bits in a bitmap.
|
|
%
|
|
:- func num_bits(bitmap) = num_bits.
|
|
% :- mode num_bits(bitmap_ui) = out is det.
|
|
:- mode num_bits(in) = out is det.
|
|
|
|
% Return the number of bytes in a bitmap, failing if the bitmap
|
|
% has a partial final byte.
|
|
%
|
|
:- func num_bytes(bitmap) = num_bytes.
|
|
% :- mode num_bytes(bitmap_ui) = out is semidet.
|
|
:- mode num_bytes(in) = out is semidet.
|
|
|
|
% As above, but throw an exception if the bitmap has a partial final byte.
|
|
%
|
|
:- func det_num_bytes(bitmap) = num_bytes.
|
|
% :- mode det_num_bytes(bitmap_ui) = out is det.
|
|
:- mode det_num_bytes(in) = out is det.
|
|
|
|
% Return the number of bits in a byte (always 8).
|
|
%
|
|
:- func bits_per_byte = int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Get or set the given bit.
|
|
% The unsafe versions do not check whether the bit is in range.
|
|
%
|
|
|
|
:- func bitmap ^ bit(bit_index) = bool.
|
|
% :- mode bitmap_ui ^ bit(in) = out is det.
|
|
:- mode in ^ bit(in) = out is det.
|
|
|
|
:- func bitmap ^ unsafe_bit(bit_index) = bool.
|
|
% :- mode bitmap_ui ^ unsafe_bit(in) = out is det.
|
|
:- mode in ^ unsafe_bit(in) = out is det.
|
|
|
|
:- func (bitmap ^ bit(bit_index) := bool) = bitmap.
|
|
:- mode (bitmap_di ^ bit(in) := in) = bitmap_uo is det.
|
|
|
|
:- func (bitmap ^ unsafe_bit(bit_index) := bool) = bitmap.
|
|
:- mode (bitmap_di ^ unsafe_bit(in) := in) = bitmap_uo is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Bitmap ^ bits(OffSet, NumBits) = Word.
|
|
% The low order bits of Word contain the NumBits bits of BitMap
|
|
% starting at OffSet.
|
|
% 0 =< NumBits =< int.bits_per_int.
|
|
%
|
|
|
|
:- func bitmap ^ bits(bit_index, num_bits) = word.
|
|
% :- mode bitmap_ui ^ bits(in, in) = out is det.
|
|
:- mode in ^ bits(in, in) = out is det.
|
|
|
|
:- func bitmap ^ unsafe_bits(bit_index, num_bits) = word.
|
|
% :- mode bitmap_ui ^ unsafe_bits(in, in) = out is det.
|
|
:- mode in ^ unsafe_bits(in, in) = out is det.
|
|
|
|
:- func (bitmap ^ bits(bit_index, num_bits) := word) = bitmap.
|
|
:- mode (bitmap_di ^ bits(in, in) := in) = bitmap_uo is det.
|
|
|
|
:- func (bitmap ^ unsafe_bits(bit_index, num_bits) := word) = bitmap.
|
|
:- mode (bitmap_di ^ unsafe_bits(in, in) := in) = bitmap_uo
|
|
is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% BM ^ byte(ByteNumber)
|
|
% Get or set the given numbered byte (multiply ByteNumber by
|
|
% bits_per_byte to get the bit index of the start of the byte).
|
|
%
|
|
% The bits are stored in or taken from the least significant bits
|
|
% of the integer.
|
|
% The unsafe versions do not check whether the byte is in range.
|
|
%
|
|
|
|
:- func bitmap ^ byte(byte_index) = byte.
|
|
% :- mode bitmap_ui ^ byte(in) = out is det.
|
|
:- mode in ^ byte(in) = out is det.
|
|
|
|
:- func bitmap ^ unsafe_byte(byte_index) = byte.
|
|
% :- mode bitmap_ui ^ unsafe_byte(in) = out is det.
|
|
:- mode in ^ unsafe_byte(in) = out is det.
|
|
|
|
:- func (bitmap ^ byte(byte_index) := byte) = bitmap.
|
|
:- mode (bitmap_di ^ byte(in) := in) = bitmap_uo is det.
|
|
|
|
:- func (bitmap ^ unsafe_byte(byte_index) := byte) = bitmap.
|
|
:- mode (bitmap_di ^ unsafe_byte(in) := in) = bitmap_uo is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Flip the given bit.
|
|
%
|
|
:- func flip(bitmap, bit_index) = bitmap.
|
|
:- mode flip(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
:- pred flip(bit_index, bitmap, bitmap).
|
|
:- mode flip(in, bitmap_di, bitmap_uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Variations that might be slightly more efficient by not
|
|
% converting bits to bool.
|
|
%
|
|
|
|
:- func set(bitmap, bit_index) = bitmap.
|
|
:- mode set(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
:- pred set(bit_index, bitmap, bitmap).
|
|
:- mode set(in, bitmap_di, bitmap_uo) is det.
|
|
|
|
:- func clear(bitmap, bit_index) = bitmap.
|
|
:- mode clear(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
:- pred clear(bit_index, bitmap, bitmap).
|
|
:- mode clear(in, bitmap_di, bitmap_uo) is det.
|
|
|
|
% is_set(BM, I) and is_clear(BM, I) succeed iff bit I in BM
|
|
% is set or clear respectively.
|
|
%
|
|
:- pred is_set(bitmap, bit_index).
|
|
% :- mode is_set(bitmap_ui, in) is semidet.
|
|
:- mode is_set(in, in) is semidet.
|
|
|
|
:- pred is_clear(bitmap, bit_index).
|
|
% :- mode is_clear(bitmap_ui, in) is semidet.
|
|
:- mode is_clear(in, in) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Unsafe versions of the above: if the index is out of range
|
|
% then behaviour is undefined and bad things are likely to happen.
|
|
%
|
|
|
|
:- func unsafe_flip(bitmap, bit_index) = bitmap.
|
|
:- mode unsafe_flip(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
:- pred unsafe_flip(bit_index, bitmap, bitmap).
|
|
:- mode unsafe_flip(in, bitmap_di, bitmap_uo) is det.
|
|
|
|
:- func unsafe_set(bitmap, bit_index) = bitmap.
|
|
:- mode unsafe_set(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
:- pred unsafe_set(bit_index, bitmap, bitmap).
|
|
:- mode unsafe_set(in, bitmap_di, bitmap_uo) is det.
|
|
|
|
:- func unsafe_clear(bitmap, bit_index) = bitmap.
|
|
:- mode unsafe_clear(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
:- pred unsafe_clear(bit_index, bitmap, bitmap).
|
|
:- mode unsafe_clear(in, bitmap_di, bitmap_uo) is det.
|
|
|
|
:- pred unsafe_is_set(bitmap, bit_index).
|
|
% :- mode unsafe_is_set(bitmap_ui, in) is semidet.
|
|
:- mode unsafe_is_set(in, in) is semidet.
|
|
|
|
:- pred unsafe_is_clear(bitmap, bit_index).
|
|
% :- mode unsafe_is_clear(bitmap_ui, in) is semidet.
|
|
:- mode unsafe_is_clear(in, in) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Create a new copy of a bitmap.
|
|
%
|
|
:- func copy(bitmap) = bitmap.
|
|
% :- mode copy(bitmap_ui) = bitmap_uo is det.
|
|
:- mode copy(in) = bitmap_uo is det.
|
|
|
|
% resize(BM, N, B) resizes bitmap BM to have N bits; if N is
|
|
% smaller than the current number of bits in BM then the excess
|
|
% are discarded. If N is larger than the current number of bits
|
|
% in BM then the new bits are set if B = yes and cleared if
|
|
% B = no.
|
|
%
|
|
:- func resize(bitmap, num_bits, bool) = bitmap.
|
|
:- mode resize(bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
% Shrink a bitmap without copying it into a smaller memory allocation.
|
|
%
|
|
:- func shrink_without_copying(bitmap, num_bits) = bitmap.
|
|
:- mode shrink_without_copying(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Slice = bitmap.slice(BM, StartIndex, NumBits)
|
|
%
|
|
% A bitmap slice represents the sub-range of a bitmap of NumBits bits
|
|
% starting at bit index StartIndex. Throws an exception if the slice
|
|
% is not within the bounds of the bitmap.
|
|
%
|
|
:- type bitmap.slice.
|
|
:- func bitmap.slice(bitmap, bit_index, num_bits) = bitmap.slice.
|
|
|
|
% As above, but use byte indices.
|
|
%
|
|
:- func bitmap.byte_slice(bitmap, byte_index, num_bytes) = bitmap.slice.
|
|
|
|
% Access functions for slices.
|
|
%
|
|
:- func slice ^ slice_bitmap = bitmap.
|
|
:- func slice ^ slice_start_bit_index = bit_index.
|
|
:- func slice ^ slice_num_bits = num_bits.
|
|
|
|
% As above, but return byte indices, throwing an exception if
|
|
% the slice doesn't start and end on a byte boundary.
|
|
%
|
|
:- func slice ^ slice_start_byte_index = byte_index.
|
|
:- func slice ^ slice_num_bytes = num_bytes.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Set operations; for binary operations the second argument is altered
|
|
% in all cases. The input bitmaps must have the same size.
|
|
%
|
|
|
|
:- func complement(bitmap) = bitmap.
|
|
:- mode complement(bitmap_di) = bitmap_uo is det.
|
|
|
|
:- func union(bitmap, bitmap) = bitmap.
|
|
% :- mode union(bitmap_ui, bitmap_di) = bitmap_uo is det.
|
|
:- mode union(in, bitmap_di) = bitmap_uo is det.
|
|
|
|
:- func intersect(bitmap, bitmap) = bitmap.
|
|
% :- mode intersect(bitmap_ui, bitmap_di) = bitmap_uo is det.
|
|
:- mode intersect(in, bitmap_di) = bitmap_uo is det.
|
|
|
|
:- func difference(bitmap, bitmap) = bitmap.
|
|
% :- mode difference(bitmap_ui, bitmap_di) = bitmap_uo is det.
|
|
:- mode difference(in, bitmap_di) = bitmap_uo is det.
|
|
|
|
:- func xor(bitmap, bitmap) = bitmap.
|
|
% :- mode xor(bitmap_ui, bitmap_di) = bitmap_uo is det.
|
|
:- mode xor(in, bitmap_di) = bitmap_uo is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Condense a list of bitmaps into a single bitmap.
|
|
%
|
|
:- func append_list(list(bitmap)) = bitmap.
|
|
:- mode append_list(in) = bitmap_uo is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Operations to copy part of a bitmap.
|
|
%
|
|
|
|
% copy_bits(SrcBM, SrcStartBit, DestBM, DestStartBit, NumBits)
|
|
%
|
|
% Overwrite NumBits bits in DestBM starting at DestStartBit with
|
|
% the NumBits bits starting at SrcStartBit in SrcBM.
|
|
%
|
|
:- func copy_bits(bitmap, bit_index, bitmap, bit_index, num_bits) = bitmap.
|
|
% :- mode copy_bits(bitmap_ui, in, bitmap_di, in, in) = bitmap_uo is det.
|
|
:- mode copy_bits(in, in, bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
% copy_bits_in_bitmap(BM, SrcStartBit, DestStartBit, NumBits)
|
|
%
|
|
% Overwrite NumBits bits starting at DestStartBit with the NumBits
|
|
% bits starting at SrcStartBit in the same bitmap.
|
|
%
|
|
:- func copy_bits_in_bitmap(bitmap, bit_index, bit_index, num_bits) = bitmap.
|
|
:- mode copy_bits_in_bitmap(bitmap_di, in, in, in) = bitmap_uo is det.
|
|
|
|
% copy_bytes(SrcBM, SrcStartByte, DestBM, DestStartByte, NumBytes)
|
|
%
|
|
% Overwrite NumBytes bytes in DestBM starting at DestStartByte with
|
|
% the NumBytes bytes starting at SrcStartByte in SrcBM.
|
|
%
|
|
:- func copy_bytes(bitmap, byte_index, bitmap, byte_index, num_bytes) = bitmap.
|
|
% :- mode copy_bytes(bitmap_ui, in, bitmap_di, in, in) = bitmap_uo is det.
|
|
:- mode copy_bytes(in, in, bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
% copy_bytes_in_bitmap(BM, SrcStartByte, DestStartByte, NumBytes)
|
|
%
|
|
% Overwrite NumBytes bytes starting at DestStartByte with the NumBytes
|
|
% bytes starting at SrcStartByte in the same bitmap.
|
|
%
|
|
:- func copy_bytes_in_bitmap(bitmap, byte_index,
|
|
byte_index, num_bytes) = bitmap.
|
|
:- mode copy_bytes_in_bitmap(bitmap_di, in, in, in) = bitmap_uo is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert a bitmap to a string of the form "<length:hex digits>",
|
|
% e.g. "<24:10AFBD>".
|
|
%
|
|
:- func to_string(bitmap) = string.
|
|
% :- mode to_string(bitmap_ui) = out is det.
|
|
:- mode to_string(in) = out is det.
|
|
|
|
% Convert a string created by to_string back into a bitmap.
|
|
%
|
|
:- func from_string(string) = bitmap.
|
|
:- mode from_string(in) = bitmap_uo is semidet.
|
|
|
|
% Convert a bitmap to a string of `1' and `0' characters, where
|
|
% the bytes are separated by `.'.
|
|
%
|
|
:- func to_byte_string(bitmap) = string.
|
|
% :- mode to_byte_string(bitmap_ui) = out is det.
|
|
:- mode to_byte_string(in) = out is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Compute a hash function for a bitmap.
|
|
%
|
|
:- func hash(bitmap) = int.
|
|
% :- mode hash(bitmap_ui) = out is det.
|
|
:- mode hash(in) = out is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- interface.
|
|
|
|
% Used by io.m.
|
|
|
|
% throw_bounds_error(BM, PredName, Index, NumBits)
|
|
%
|
|
:- pred throw_bounds_error(bitmap::in, string::in, bit_index::in, num_bits::in)
|
|
is erroneous.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module char.
|
|
:- import_module exception.
|
|
:- import_module int.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init(N, B) = BM :-
|
|
( if N < 0 then
|
|
throw_bitmap_error("bitmap.init: negative size") = _ : int
|
|
else
|
|
X = initializer(B),
|
|
BM0 = initialize_bitmap(allocate_bitmap(N), N, X),
|
|
BM = clear_filler_bits(BM0)
|
|
).
|
|
|
|
init(N) = init(N, no).
|
|
|
|
:- func clear_filler_bits(bitmap) = bitmap.
|
|
:- mode clear_filler_bits(bitmap_di) = bitmap_uo is det.
|
|
|
|
clear_filler_bits(BM) = set_trailing_bits_in_byte(BM, num_bits(BM) - 1, 0).
|
|
|
|
:- func set_trailing_bits_in_byte(bitmap, bit_index, byte) = bitmap.
|
|
:- mode set_trailing_bits_in_byte(bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
set_trailing_bits_in_byte(!.BM, Bit, Initializer) = !:BM :-
|
|
FirstTrailingBit = Bit + 1,
|
|
FirstTrailingBitIndex = bit_index_in_byte(FirstTrailingBit),
|
|
( if FirstTrailingBitIndex \= 0 then
|
|
ByteIndex = byte_index_for_bit(FirstTrailingBit),
|
|
NumBitsToSet = bits_per_byte - FirstTrailingBitIndex,
|
|
!:BM = !.BM ^ unsafe_byte(ByteIndex) :=
|
|
set_bits_in_byte(!.BM ^ unsafe_byte(ByteIndex),
|
|
FirstTrailingBitIndex, NumBitsToSet, Initializer)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- func initializer(bool) = byte.
|
|
|
|
initializer(no) = 0.
|
|
initializer(yes) = \0.
|
|
|
|
:- func initialize_bitmap(bitmap, num_bits, byte) = bitmap.
|
|
:- mode initialize_bitmap(bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
initialize_bitmap(Bitmap, N, Init) =
|
|
initialize_bitmap_bytes(Bitmap, 0, byte_index_for_bit(N - 1), Init).
|
|
|
|
:- func initialize_bitmap_bytes(bitmap, byte_index, byte_index,
|
|
byte) = bitmap.
|
|
:- mode initialize_bitmap_bytes(bitmap_di, in, in, in) = bitmap_uo is det.
|
|
|
|
initialize_bitmap_bytes(BM, ByteIndex, LastByteIndex, Init) =
|
|
( if ByteIndex > LastByteIndex then
|
|
BM
|
|
else
|
|
initialize_bitmap_bytes(BM ^ unsafe_byte(ByteIndex) := Init,
|
|
ByteIndex + 1, LastByteIndex, Init)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
in_range(BM, I) :-
|
|
0 =< I, I < num_bits(BM).
|
|
|
|
:- pred in_range_rexcl(bitmap, bit_index).
|
|
:- mode in_range_rexcl(in, in) is semidet.
|
|
|
|
in_range_rexcl(BM, I) :-
|
|
0 =< I, I =< num_bits(BM).
|
|
|
|
byte_in_range(BM, I) :-
|
|
in_range(BM, I * bits_per_byte + bits_per_byte - 1).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
num_bits(_) = _ :-
|
|
private_builtin.sorry("bitmap.num_bits").
|
|
|
|
:- pragma foreign_proc("C",
|
|
num_bits(BM::in) = (NumBits::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
NumBits = BM->num_bits;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
num_bits(BM::in) = (NumBits::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
NumBits = BM.num_bits;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
num_bits(BM::in) = (NumBits::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
NumBits = BM.num_bits;
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
num_bits(BM::in) = (NumBits::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
{_, NumBits} = BM
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
num_bytes(BM) = Bytes :-
|
|
NumBits = BM ^ num_bits,
|
|
NumBits `unchecked_rem` bits_per_byte = 0,
|
|
Bytes = NumBits `unchecked_quotient` bits_per_byte.
|
|
|
|
det_num_bytes(BM) = Bytes :-
|
|
( if Bytes0 = num_bytes(BM) then
|
|
Bytes = Bytes0
|
|
else
|
|
throw_bitmap_error(
|
|
"bitmap.det_num_bytes: bitmap has a partial final byte")
|
|
).
|
|
|
|
bits_per_byte = 8.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
BM ^ bit(I) =
|
|
( if in_range(BM, I) then
|
|
BM ^ unsafe_bit(I)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.bit", I)
|
|
).
|
|
|
|
BM ^ unsafe_bit(I) =
|
|
( if unsafe_is_set(BM, I) then yes else no ).
|
|
|
|
(BM ^ bit(I) := B) =
|
|
( if in_range(BM, I) then
|
|
BM ^ unsafe_bit(I) := B
|
|
else
|
|
throw_bounds_error(BM, "bitmap.'bit :='", I)
|
|
).
|
|
|
|
(BM ^ unsafe_bit(I) := yes) = unsafe_set(BM, I).
|
|
(BM ^ unsafe_bit(I) := no) = unsafe_clear(BM, I).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
BM ^ bits(FirstBit, NumBits) =
|
|
( if
|
|
FirstBit >= 0,
|
|
NumBits >= 0,
|
|
NumBits =< int.bits_per_int,
|
|
in_range_rexcl(BM, FirstBit + NumBits)
|
|
then
|
|
BM ^ unsafe_bits(FirstBit, NumBits)
|
|
else if
|
|
( NumBits < 0
|
|
; NumBits > int.bits_per_int
|
|
)
|
|
then
|
|
throw_bitmap_error("bitmap.bits: number of bits must be between " ++
|
|
"0 and `int.bits_per_int'.")
|
|
else
|
|
throw_bounds_error(BM, "bitmap.bits", FirstBit)
|
|
).
|
|
|
|
BM ^ unsafe_bits(FirstBit, NumBits) = Bits :-
|
|
FirstByteIndex = byte_index_for_bit(FirstBit),
|
|
FirstBitIndex = bit_index_in_byte(FirstBit),
|
|
extract_bits_from_bytes(FirstByteIndex, FirstBitIndex,
|
|
NumBits, BM, 0, Bits).
|
|
|
|
% Extract the given number of bits starting at the most significant.
|
|
%
|
|
:- pred extract_bits_from_bytes(byte_index, bit_index_in_byte, num_bits,
|
|
bitmap, word, word).
|
|
% :- mode extract_bits_from_bytes(in, in, in, bitmap_ui, in, out) is det.
|
|
:- mode extract_bits_from_bytes(in, in, in, in, in, out) is det.
|
|
|
|
extract_bits_from_bytes(FirstByteIndex, FirstBitIndex, NumBits, BM, !Bits) :-
|
|
RemainingBitsInByte = bits_per_byte - FirstBitIndex,
|
|
( if
|
|
NumBits > RemainingBitsInByte
|
|
then
|
|
NumBitsThisByte = RemainingBitsInByte,
|
|
extract_bits_from_byte_index(FirstByteIndex, FirstBitIndex,
|
|
NumBitsThisByte, BM, !Bits),
|
|
extract_bits_from_bytes(FirstByteIndex + 1, 0,
|
|
NumBits - NumBitsThisByte, BM, !Bits)
|
|
else if
|
|
NumBits > 0
|
|
then
|
|
extract_bits_from_byte_index(FirstByteIndex, FirstBitIndex,
|
|
NumBits, BM, !Bits)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred extract_bits_from_byte_index(byte_index, bit_index_in_byte, num_bits,
|
|
bitmap, word, word).
|
|
% :- mode extract_bits_from_byte_index(in, in, in, bitmap_ui, in, out) is det.
|
|
:- mode extract_bits_from_byte_index(in, in, in, in, in, out) is det.
|
|
|
|
extract_bits_from_byte_index(ByteIndex, FirstBitIndex,
|
|
NumBitsThisByte, BM, !Bits) :-
|
|
BitsThisByte = extract_bits_from_byte(BM ^ unsafe_byte(ByteIndex),
|
|
FirstBitIndex, NumBitsThisByte),
|
|
!:Bits = (!.Bits `unchecked_left_shift` NumBitsThisByte) \/ BitsThisByte.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
(BM ^ bits(FirstBit, NumBits) := Bits) =
|
|
( if
|
|
FirstBit >= 0,
|
|
NumBits >= 0,
|
|
NumBits =< int.bits_per_int,
|
|
in_range_rexcl(BM, FirstBit + NumBits)
|
|
then
|
|
BM ^ unsafe_bits(FirstBit, NumBits) := Bits
|
|
else if
|
|
( NumBits < 0
|
|
; NumBits > int.bits_per_int
|
|
)
|
|
then
|
|
throw_bitmap_error(
|
|
"bitmap.'bits :=': number of bits must be between " ++
|
|
"0 and `int.bits_per_int'.")
|
|
else
|
|
throw_bounds_error(BM, "bitmap.'bits :='", FirstBit)
|
|
).
|
|
|
|
(BM0 ^ unsafe_bits(FirstBit, NumBits) := Bits) = BM :-
|
|
LastBit = FirstBit + NumBits - 1,
|
|
LastByteIndex = byte_index_for_bit(LastBit),
|
|
LastBitIndex = bit_index_in_byte(LastBit),
|
|
set_bits_in_bytes(LastByteIndex, LastBitIndex, NumBits, Bits, BM0, BM).
|
|
|
|
% Set the given number of bits starting at the least significant.
|
|
%
|
|
:- pred set_bits_in_bytes(byte_index, bit_index_in_byte, num_bits,
|
|
word, bitmap, bitmap).
|
|
:- mode set_bits_in_bytes(in, in, in, in, bitmap_di, bitmap_uo) is det.
|
|
|
|
set_bits_in_bytes(LastByteIndex, LastBitIndex, NumBits, Bits, !BM) :-
|
|
RemainingBitsInByte = LastBitIndex + 1,
|
|
( if NumBits > RemainingBitsInByte then
|
|
NumBitsThisByte = RemainingBitsInByte,
|
|
set_bits_in_byte_index(LastByteIndex, LastBitIndex, NumBitsThisByte,
|
|
Bits, !BM),
|
|
set_bits_in_bytes(LastByteIndex - 1, bits_per_byte - 1,
|
|
NumBits - NumBitsThisByte,
|
|
Bits `unchecked_right_shift` NumBitsThisByte, !BM)
|
|
else if NumBits > 0 then
|
|
set_bits_in_byte_index(LastByteIndex, LastBitIndex, NumBits, Bits, !BM)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred set_bits_in_byte_index(byte_index, bit_index_in_byte, num_bits,
|
|
word, bitmap, bitmap).
|
|
:- mode set_bits_in_byte_index(in, in, in, in, bitmap_di, bitmap_uo) is det.
|
|
|
|
set_bits_in_byte_index(ByteIndex, LastBitIndex, NumBitsThisByte, Bits, !BM) :-
|
|
FirstBitInByte = LastBitIndex - NumBitsThisByte + 1,
|
|
!:BM = !.BM ^ unsafe_byte(ByteIndex) :=
|
|
set_bits_in_byte(!.BM ^ unsafe_byte(ByteIndex),
|
|
FirstBitInByte, NumBitsThisByte, Bits).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
BM ^ byte(N) =
|
|
( if N >= 0, in_range(BM, N * bits_per_byte + bits_per_byte - 1) then
|
|
BM ^ unsafe_byte(N)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.byte", N)
|
|
).
|
|
|
|
_ ^ unsafe_byte(_) = _ :-
|
|
private_builtin.sorry("bitmap.unsafe_byte").
|
|
|
|
:- pragma foreign_proc("C",
|
|
unsafe_byte(N::in, BM::in) = (Byte::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
Byte = (MR_Integer) BM->elements[N];
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
unsafe_byte(N::in, BM::in) = (Byte::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
// Mask off sign bits so Byte is in range 0-255.
|
|
Byte = ((int) BM.elements[N]) & 0xff;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
unsafe_byte(N::in, BM::in) = (Byte::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
Byte = BM.elements[N];
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
unsafe_byte(N::in, BM::in) = (Byte::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
{Bin, _} = BM,
|
|
<<_:N/binary, Byte/integer, _/binary>> = Bin
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
(BM ^ byte(N) := Byte) =
|
|
( if N >= 0, in_range(BM, N * bits_per_byte + bits_per_byte - 1) then
|
|
BM ^ unsafe_byte(N) := Byte
|
|
else
|
|
throw_bounds_error(BM, "bitmap.'byte :='", N)
|
|
).
|
|
|
|
(_ ^ unsafe_byte(_) := _) = _ :-
|
|
private_builtin.sorry("bitmap.'unsafe_byte :='").
|
|
|
|
:- pragma foreign_proc("C",
|
|
'unsafe_byte :='(N::in, BM0::bitmap_di, Byte::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = BM0;
|
|
BM->elements[N] = (MR_uint_least8_t) Byte;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
'unsafe_byte :='(N::in, BM0::bitmap_di, Byte::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = BM0;
|
|
BM.elements[N] = (byte) Byte;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
'unsafe_byte :='(N::in, BM0::bitmap_di, Byte::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = BM0;
|
|
BM.elements[N] = (byte) Byte;
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
'unsafe_byte :='(N::in, BM0::bitmap_di, Byte::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
{Bin0, NumBits} = BM0,
|
|
<<Left:N/binary, _/integer, Right/binary>> = Bin0,
|
|
Bin = <<Left/binary, Byte/integer, Right/binary>>,
|
|
BM = {Bin, NumBits}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
flip(BM, I) =
|
|
( if in_range(BM, I) then
|
|
unsafe_flip(BM, I)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.flip", I)
|
|
).
|
|
|
|
flip(I, BM, flip(BM, I)).
|
|
|
|
set(BM, I) =
|
|
( if in_range(BM, I) then
|
|
unsafe_set(BM, I)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.set", I)
|
|
).
|
|
|
|
set(I, BM, set(BM, I)).
|
|
|
|
clear(BM, I) =
|
|
( if in_range(BM, I) then
|
|
unsafe_clear(BM, I)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.clear", I)
|
|
).
|
|
|
|
clear(I, BM, clear(BM, I)).
|
|
|
|
is_set(BM, I) :-
|
|
( if in_range(BM, I) then
|
|
unsafe_is_set(BM, I)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.is_set", I) = _ : int
|
|
).
|
|
|
|
is_clear(BM, I) :-
|
|
( if in_range(BM, I) then
|
|
unsafe_is_clear(BM, I)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.is_clear", I) = _ : int
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
unsafe_flip(BM0, I) = BM :-
|
|
ByteIndex = byte_index_for_bit(I),
|
|
Byte0 = BM0 ^ unsafe_byte(ByteIndex),
|
|
Byte = Byte0 `xor` bitmask(I),
|
|
BM = BM0 ^ unsafe_byte(ByteIndex) := Byte.
|
|
|
|
unsafe_flip(I, BM, unsafe_flip(BM, I)).
|
|
|
|
unsafe_set(BM0, I) = BM :-
|
|
ByteIndex = byte_index_for_bit(I),
|
|
Byte0 = BM0 ^ unsafe_byte(ByteIndex),
|
|
Byte = Byte0 \/ bitmask(I),
|
|
BM = BM0 ^ unsafe_byte(ByteIndex) := Byte.
|
|
|
|
unsafe_set(I, BM, unsafe_set(BM, I)).
|
|
|
|
unsafe_clear(BM0, I) = BM :-
|
|
ByteIndex = byte_index_for_bit(I),
|
|
Byte0 = BM0 ^ unsafe_byte(ByteIndex),
|
|
Byte = Byte0 /\ \bitmask(I),
|
|
BM = BM0 ^ unsafe_byte(ByteIndex) := Byte.
|
|
|
|
unsafe_clear(I, BM, unsafe_clear(BM, I)).
|
|
|
|
unsafe_is_set(BM, I) :-
|
|
not unsafe_is_clear(BM, I).
|
|
|
|
unsafe_is_clear(BM, I) :-
|
|
BM ^ unsafe_byte(byte_index_for_bit(I)) /\ bitmask(I) = 0.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
copy(BM0::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
MR_allocate_bitmap_msg(BM, BM0->num_bits, MR_ALLOC_ID);
|
|
MR_copy_bitmap(BM, BM0);
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
copy(BM0::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = new mercury.runtime.MercuryBitmap(BM0.num_bits);
|
|
System.Array.Copy(BM0.elements, 0, BM.elements, 0, BM0.elements.Length);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
copy(BM0::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = new MercuryBitmap(BM0.num_bits);
|
|
System.arraycopy(BM0.elements, 0, BM.elements, 0, BM0.elements.length);
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
copy(BM0::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = BM0
|
|
").
|
|
|
|
copy(BM0) = BM :-
|
|
NumBits = BM0 ^ num_bits,
|
|
BM = clear_filler_bits(
|
|
unsafe_copy_bits(0, BM0, 0, allocate_bitmap(NumBits), 0, NumBits)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
resize(!.BM, NewSize, InitializerBit) = !:BM :-
|
|
( if NewSize =< 0 then
|
|
!:BM = init(NewSize, InitializerBit)
|
|
else
|
|
OldSize = num_bits(!.BM),
|
|
InitializerByte = initializer(InitializerBit),
|
|
!:BM = resize_bitmap(!.BM, NewSize),
|
|
( if NewSize > OldSize then
|
|
% Fill in the trailing bits in the previous final byte.
|
|
!:BM = set_trailing_bits_in_byte(!.BM, OldSize - 1,
|
|
InitializerByte),
|
|
OldLastByteIndex = byte_index_for_bit(OldSize - 1),
|
|
NewLastByteIndex = byte_index_for_bit(NewSize - 1),
|
|
( if NewLastByteIndex > OldLastByteIndex then
|
|
!:BM = initialize_bitmap_bytes(!.BM, OldLastByteIndex + 1,
|
|
NewLastByteIndex, InitializerByte)
|
|
else
|
|
true
|
|
)
|
|
else
|
|
true
|
|
),
|
|
!:BM = clear_filler_bits(!.BM)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
shrink_without_copying(!.BM, NewSize) = !:BM :-
|
|
( if 0 =< NewSize, NewSize =< !.BM ^ num_bits then
|
|
!:BM = !.BM ^ num_bits := NewSize
|
|
else
|
|
throw_bounds_error(!.BM,
|
|
"bitmap.shrink_without_copying", NewSize) = _ : int
|
|
).
|
|
|
|
:- func 'num_bits :='(bitmap, num_bits) = bitmap.
|
|
:- mode 'num_bits :='(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
'num_bits :='(_, _) = _
|
|
:- private_builtin.sorry("bitmap.'num_bits :='").
|
|
|
|
:- pragma foreign_proc("C",
|
|
'num_bits :='(BM0::bitmap_di, NumBits::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = BM0;
|
|
BM->num_bits = NumBits;
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
'num_bits :='(BM0::bitmap_di, NumBits::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = BM0;
|
|
BM.num_bits = NumBits;
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
'num_bits :='(BM0::bitmap_di, NumBits::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = BM0;
|
|
BM.num_bits = NumBits;
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
'num_bits :='(BM0::bitmap_di, NumBits::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
{Bin, _} = BM0,
|
|
BM = {Bin, NumBits}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type bitmap.slice
|
|
---> bitmap.slice_ctor(
|
|
slice_bitmap_field :: bitmap,
|
|
slice_start_bit_index_field :: bit_index,
|
|
slice_num_bits_field :: num_bits
|
|
).
|
|
|
|
slice(BM, StartBit, NumBits) = Slice :-
|
|
( if
|
|
NumBits >= 0,
|
|
StartBit >= 0,
|
|
in_range_rexcl(BM, StartBit + NumBits)
|
|
then
|
|
Slice = bitmap.slice_ctor(BM, StartBit, NumBits)
|
|
else
|
|
throw_bounds_error(BM, "bitmap.slice", StartBit, NumBits)
|
|
).
|
|
|
|
byte_slice(BM, StartByte, NumBytes) =
|
|
slice(BM, StartByte * bits_per_byte, NumBytes * bits_per_byte).
|
|
|
|
Slice ^ slice_bitmap = Slice ^ slice_bitmap_field.
|
|
Slice ^ slice_start_bit_index = Slice ^ slice_start_bit_index_field.
|
|
Slice ^ slice_num_bits = Slice ^ slice_num_bits_field.
|
|
|
|
Slice ^ slice_start_byte_index =
|
|
quotient_bits_per_byte_with_rem_zero("bitmap.slice_start_byte_index",
|
|
Slice ^ slice_start_bit_index).
|
|
Slice ^ slice_num_bytes =
|
|
quotient_bits_per_byte_with_rem_zero("bitmap.slice_num_bytes",
|
|
Slice ^ slice_num_bits).
|
|
|
|
:- func quotient_bits_per_byte_with_rem_zero(string, int) = int is det.
|
|
|
|
quotient_bits_per_byte_with_rem_zero(Pred, Int) = Quotient :-
|
|
( if unchecked_rem(Int, bits_per_byte) = 0 then
|
|
Quotient = unchecked_quotient(Int, bits_per_byte)
|
|
else
|
|
throw_bitmap_error(Pred ++ ": not a byte slice.")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
complement(BM) =
|
|
clear_filler_bits(complement_2(byte_index_for_bit(num_bits(BM) - 1), BM)).
|
|
|
|
:- func complement_2(int, bitmap) = bitmap.
|
|
:- mode complement_2(in, bitmap_di) = bitmap_uo is det.
|
|
|
|
complement_2(ByteI, BM0) = BM :-
|
|
( if
|
|
ByteI < 0
|
|
then
|
|
BM = BM0
|
|
else
|
|
X = BM0 ^ unsafe_byte(ByteI),
|
|
BM1 = BM0 ^ unsafe_byte(ByteI) := \ X,
|
|
BM = complement_2(ByteI - 1, BM1)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
union(BMa, BMb) =
|
|
( if num_bits(BMa) = num_bits(BMb) then
|
|
zip((\/), BMa, BMb)
|
|
else
|
|
throw_bitmap_error("bitmap.union: bitmaps not the same size")
|
|
).
|
|
|
|
intersect(BMa, BMb) =
|
|
( if num_bits(BMa) = num_bits(BMb) then
|
|
zip((/\), BMa, BMb)
|
|
else
|
|
throw_bitmap_error("bitmap.intersect: bitmaps not the same size")
|
|
).
|
|
|
|
difference(BMa, BMb) =
|
|
( if num_bits(BMa) = num_bits(BMb) then
|
|
zip((func(X, Y) = (X /\ \Y)), BMa, BMb)
|
|
else
|
|
throw_bitmap_error("bitmap.difference: bitmaps not the same size")
|
|
).
|
|
|
|
xor(BMa, BMb) =
|
|
( if num_bits(BMa) = num_bits(BMb) then
|
|
zip((func(X, Y) = (X `xor` Y)), BMa, BMb)
|
|
else
|
|
throw_bitmap_error("bitmap.xor: bitmaps not the same size")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Applies a function to every corresponding element between +ve I
|
|
% and 0 inclusive, destructively updating the second bitmap.
|
|
%
|
|
:- func zip(func(byte, byte) = byte,
|
|
bitmap, bitmap) = bitmap.
|
|
% :- mode zip(func(in, in) = out is det,
|
|
% bitmap_ui, bitmap_di) = bitmap_uo is det.
|
|
:- mode zip(func(in, in) = out is det,
|
|
in, bitmap_di) = bitmap_uo is det.
|
|
|
|
zip(Fn, BMa, BMb) =
|
|
( if num_bits(BMb) = 0 then
|
|
BMb
|
|
else
|
|
zip2(byte_index_for_bit(num_bits(BMb) - 1), Fn, BMa, BMb)
|
|
).
|
|
|
|
:- func zip2(byte_index, func(byte, byte) = byte,
|
|
bitmap, bitmap) = bitmap.
|
|
% :- mode zip2(in, func(in, in) = out is det,
|
|
% bitmap_ui, bitmap_di) = bitmap_uo is det.
|
|
:- mode zip2(in, func(in, in) = out is det,
|
|
in, bitmap_di) = bitmap_uo is det.
|
|
|
|
zip2(I, Fn, BMa, BMb) = BM :-
|
|
( if I >= 0 then
|
|
Xa = BMa ^ unsafe_byte(I),
|
|
Xb = BMb ^ unsafe_byte(I),
|
|
BMc = BMb ^ unsafe_byte(I) := Fn(Xa, Xb),
|
|
BM = zip2(I - 1, Fn, BMa, BMc)
|
|
else
|
|
BM = BMb
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
append_list(BMs) = !:BM :-
|
|
BMSize = list.foldl((func(BM, Size) = Size + BM ^ num_bits), BMs, 0),
|
|
!:BM = init(BMSize),
|
|
list.foldl2(copy_bitmap_into_place, BMs, 0, _, !BM).
|
|
|
|
:- pred copy_bitmap_into_place(bitmap::in, int::in, int::out,
|
|
bitmap::bitmap_di, bitmap::bitmap_uo) is det.
|
|
|
|
copy_bitmap_into_place(ThisBM, !Index, !BM) :-
|
|
!:BM = unsafe_copy_bits(0, ThisBM, 0, !.BM, !.Index, ThisBM ^ num_bits),
|
|
!:Index = !.Index + ThisBM ^ num_bits.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
copy_bits(SrcBM, SrcStartBit, DestBM, DestStartBit, NumBits) =
|
|
copy_bits(0, SrcBM, SrcStartBit, DestBM, DestStartBit, NumBits).
|
|
|
|
copy_bits_in_bitmap(SrcBM, SrcStartBit, DestStartBit, NumBits) =
|
|
copy_bits(1, SrcBM, SrcStartBit, SrcBM, DestStartBit, NumBits).
|
|
|
|
:- func copy_bits(int, bitmap, bit_index,
|
|
bitmap, bit_index, num_bits) = bitmap.
|
|
% :- mode copy_bits(in, bitmap_ui, in, bitmap_di, in, in) = bitmap_uo is det.
|
|
:- mode copy_bits(in, in, in, bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
copy_bits(SameBM, SrcBM, SrcStartBit, DestBM, DestStartBit, NumBits) =
|
|
( if
|
|
NumBits >= 0,
|
|
SrcStartBit >= 0,
|
|
DestStartBit >= 0,
|
|
in_range_rexcl(SrcBM, SrcStartBit + NumBits),
|
|
in_range_rexcl(DestBM, DestStartBit + NumBits)
|
|
then
|
|
unsafe_copy_bits(SameBM, SrcBM, SrcStartBit,
|
|
DestBM, DestStartBit, NumBits)
|
|
else
|
|
( if
|
|
( NumBits < 0
|
|
; SrcStartBit < 0
|
|
; not in_range(SrcBM, SrcStartBit + NumBits - 1)
|
|
)
|
|
then
|
|
throw_bounds_error(SrcBM, "copy_bits (source)",
|
|
SrcStartBit, NumBits)
|
|
else if
|
|
( DestStartBit < 0
|
|
; not in_range(DestBM, DestStartBit + NumBits - 1)
|
|
)
|
|
then
|
|
throw_bounds_error(DestBM, "copy_bits (destination)",
|
|
DestStartBit, NumBits)
|
|
else
|
|
throw_bitmap_error("bitmap.copy_bits: failed to diagnose error")
|
|
)
|
|
).
|
|
|
|
:- func unsafe_copy_bits(int, bitmap, bit_index,
|
|
bitmap, bit_index, num_bits) = bitmap.
|
|
% :- mode unsafe_copy_bits(in, bitmap_ui, in,
|
|
% bitmap_di, in, in) = bitmap_uo is det.
|
|
:- mode unsafe_copy_bits(in, in, in,
|
|
bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
unsafe_copy_bits(SameBM, SrcBM, SrcStartBit, !.DestBM, DestStartBit,
|
|
!.NumBits) = !:DestBM :-
|
|
SrcStartIndex = bit_index_in_byte(SrcStartBit),
|
|
DestStartIndex = bit_index_in_byte(DestStartBit),
|
|
( if !.NumBits < 2 * bits_per_byte then
|
|
% The alternatives below don't handle ranges that don't span
|
|
% a byte boundary.
|
|
!:DestBM = !.DestBM ^ unsafe_bits(DestStartBit, !.NumBits) :=
|
|
SrcBM ^ unsafe_bits(SrcStartBit, !.NumBits)
|
|
else if SrcStartIndex = DestStartIndex then
|
|
% Handle the common case where the bits to be moved have
|
|
% the same offsets in each byte, so we can do a block byte copy.
|
|
StartIndex = SrcStartIndex,
|
|
SrcEndBit = SrcStartBit + !.NumBits - 1,
|
|
EndIndex = bit_index_in_byte(SrcEndBit),
|
|
( if
|
|
StartIndex = 0,
|
|
EndIndex = bits_per_byte - 1
|
|
then
|
|
% It's an aligned block of bytes, move it.
|
|
NumBytes = !.NumBits `unchecked_quotient` bits_per_byte,
|
|
SrcStartByteIndex = SrcStartBit `unchecked_quotient` bits_per_byte,
|
|
DestStartByteIndex =
|
|
DestStartBit `unchecked_quotient` bits_per_byte,
|
|
!:DestBM = unsafe_copy_bytes(SameBM, SrcBM, SrcStartByteIndex,
|
|
!.DestBM, DestStartByteIndex, NumBytes)
|
|
else
|
|
% Grab the odd bits at each end of the block to move,
|
|
% leaving a block of aligned bytes to move.
|
|
( if StartIndex = 0 then
|
|
NumBitsAtStart = 0,
|
|
StartBitsToSet = 0
|
|
else
|
|
NumBitsAtStart = bits_per_byte - StartIndex,
|
|
SrcPartialStartByteIndex = byte_index_for_bit(SrcStartBit),
|
|
StartBitsToSet =
|
|
extract_bits_from_byte(
|
|
SrcBM ^ unsafe_byte(SrcPartialStartByteIndex),
|
|
StartIndex, NumBitsAtStart),
|
|
|
|
!:NumBits = !.NumBits - NumBitsAtStart
|
|
),
|
|
|
|
( if EndIndex = bits_per_byte - 1 then
|
|
NumBitsAtEnd = 0,
|
|
EndBitsToSet = 0
|
|
else
|
|
NumBitsAtEnd = EndIndex + 1,
|
|
SrcPartialEndByteIndex = byte_index_for_bit(SrcEndBit),
|
|
EndBitsToSet =
|
|
extract_bits_from_byte(
|
|
SrcBM ^ unsafe_byte(SrcPartialEndByteIndex),
|
|
0, NumBitsAtEnd),
|
|
|
|
!:NumBits = !.NumBits - NumBitsAtEnd
|
|
),
|
|
|
|
% Do the block copy.
|
|
NumBytes = !.NumBits `unchecked_quotient` bits_per_byte,
|
|
SrcStartByteIndex = (SrcStartBit + NumBitsAtStart)
|
|
`unchecked_quotient` bits_per_byte,
|
|
DestStartByteIndex = (DestStartBit + NumBitsAtStart)
|
|
`unchecked_quotient` bits_per_byte,
|
|
!:DestBM = unsafe_copy_bytes(SameBM, SrcBM, SrcStartByteIndex,
|
|
!.DestBM, DestStartByteIndex, NumBytes),
|
|
|
|
% Fill in the partial bytes at the start and end of the range.
|
|
( if NumBitsAtStart = 0 then
|
|
true
|
|
else
|
|
DestPartialStartByteIndex = DestStartByteIndex - 1,
|
|
!:DestBM =
|
|
!.DestBM ^ unsafe_byte(DestPartialStartByteIndex) :=
|
|
set_bits_in_byte(
|
|
!.DestBM ^ unsafe_byte(DestPartialStartByteIndex),
|
|
StartIndex, NumBitsAtStart, StartBitsToSet)
|
|
),
|
|
|
|
( if NumBitsAtEnd = 0 then
|
|
true
|
|
else
|
|
DestPartialEndByteIndex = DestStartByteIndex + NumBytes,
|
|
!:DestBM =
|
|
!.DestBM ^ unsafe_byte(DestPartialEndByteIndex) :=
|
|
set_bits_in_byte(
|
|
!.DestBM ^ unsafe_byte(DestPartialEndByteIndex),
|
|
0, NumBitsAtEnd, EndBitsToSet)
|
|
)
|
|
)
|
|
else
|
|
!:DestBM = unsafe_copy_unaligned_bits(SameBM, SrcBM, SrcStartBit,
|
|
!.DestBM, DestStartBit, !.NumBits)
|
|
).
|
|
|
|
copy_bytes(SrcBM, SrcStartByteIndex, DestBM, DestStartByteIndex, NumBytes) =
|
|
copy_bytes(0, SrcBM, SrcStartByteIndex,
|
|
DestBM, DestStartByteIndex, NumBytes).
|
|
|
|
copy_bytes_in_bitmap(SrcBM, SrcStartByteIndex, DestStartByteIndex, NumBytes) =
|
|
copy_bytes(1, SrcBM, SrcStartByteIndex,
|
|
SrcBM, DestStartByteIndex, NumBytes).
|
|
|
|
% The SameBM parameter is 1 if we are copying within the same bitmap
|
|
% We use an `int' rather than a `bool' for easier interfacing with C.
|
|
%
|
|
:- func copy_bytes(int, bitmap, bit_index,
|
|
bitmap, bit_index, num_bits) = bitmap.
|
|
% :- mode copy_bytes(in, bitmap_ui, in,
|
|
% bitmap_di, in, in) = bitmap_uo is det.
|
|
:- mode copy_bytes(in, in, in,
|
|
bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
copy_bytes(SameBM, SrcBM, SrcStartByte, DestBM, DestStartByte, NumBytes) =
|
|
( if
|
|
NumBytes = 0
|
|
then
|
|
DestBM
|
|
else if
|
|
NumBytes > 0,
|
|
SrcStartByte >= 0,
|
|
byte_in_range(SrcBM, SrcStartByte + NumBytes - 1),
|
|
DestStartByte >= 0,
|
|
byte_in_range(DestBM, DestStartByte + NumBytes - 1)
|
|
then
|
|
unsafe_copy_bytes(SameBM, SrcBM, SrcStartByte, DestBM,
|
|
DestStartByte, NumBytes)
|
|
else
|
|
throw_bitmap_error("bitmap.copy_bytes: out of range")
|
|
).
|
|
|
|
:- func unsafe_copy_bytes(int, bitmap, byte_index,
|
|
bitmap, byte_index, num_bytes) = bitmap.
|
|
% :- mode unsafe_copy_bytes(in, bitmap_ui, in,
|
|
% bitmap_di, in, in) = bitmap_uo is det.
|
|
:- mode unsafe_copy_bytes(in, in, in,
|
|
bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
unsafe_copy_bytes(SameBM::in, SrcBM::in, SrcFirstByteIndex::in,
|
|
DestBM0::bitmap_di, DestFirstByteIndex::in,
|
|
NumBytes::in) = (DestBM::bitmap_uo),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
DestBM = DestBM0;
|
|
if (SameBM) {
|
|
memmove(DestBM->elements + DestFirstByteIndex,
|
|
SrcBM->elements + SrcFirstByteIndex, NumBytes);
|
|
} else {
|
|
MR_memcpy(DestBM->elements + DestFirstByteIndex,
|
|
SrcBM->elements + SrcFirstByteIndex, NumBytes);
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
unsafe_copy_bytes(_SameBM::in, SrcBM::in, SrcFirstByteIndex::in,
|
|
DestBM0::bitmap_di, DestFirstByteIndex::in,
|
|
NumBytes::in) = (DestBM::bitmap_uo),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
DestBM = DestBM0;
|
|
System.Array.Copy(SrcBM.elements, SrcFirstByteIndex,
|
|
DestBM.elements, DestFirstByteIndex, NumBytes);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
unsafe_copy_bytes(_SameBM::in, SrcBM::in, SrcFirstByteIndex::in,
|
|
DestBM0::bitmap_di, DestFirstByteIndex::in, NumBytes::in)
|
|
= (DestBM::bitmap_uo),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
DestBM = DestBM0;
|
|
System.arraycopy(SrcBM.elements, SrcFirstByteIndex,
|
|
DestBM.elements, DestFirstByteIndex, NumBytes);
|
|
").
|
|
|
|
unsafe_copy_bytes(SameBM, SrcBM, SrcFirstByteIndex,
|
|
!.DestBM, DestFirstByteIndex, NumBytes) = !:DestBM :-
|
|
Direction = choose_copy_direction(SameBM, SrcFirstByteIndex,
|
|
DestFirstByteIndex),
|
|
(
|
|
Direction = left_to_right,
|
|
!:DestBM = unsafe_do_copy_bytes(SrcBM, SrcFirstByteIndex,
|
|
!.DestBM, DestFirstByteIndex, NumBytes, 1)
|
|
;
|
|
Direction = right_to_left,
|
|
!:DestBM = unsafe_do_copy_bytes(SrcBM,
|
|
SrcFirstByteIndex + NumBytes - 1,
|
|
!.DestBM, DestFirstByteIndex + NumBytes - 1, NumBytes, -1)
|
|
).
|
|
|
|
:- func unsafe_do_copy_bytes(bitmap, byte_index,
|
|
bitmap, byte_index, num_bytes, num_bytes) = bitmap.
|
|
% :- mode unsafe_do_copy_bytes(bitmap_ui, in,
|
|
% bitmap_di, in, in, in) = bitmap_uo is det.
|
|
:- mode unsafe_do_copy_bytes(in, in,
|
|
bitmap_di, in, in, in) = bitmap_uo is det.
|
|
|
|
unsafe_do_copy_bytes(SrcBM, SrcByteIndex, DestBM, DestByteIndex,
|
|
NumBytes, AddForNext) =
|
|
( if NumBytes = 0 then
|
|
DestBM
|
|
else
|
|
unsafe_do_copy_bytes(SrcBM, SrcByteIndex + AddForNext,
|
|
DestBM ^ unsafe_byte(DestByteIndex) :=
|
|
SrcBM ^ unsafe_byte(SrcByteIndex),
|
|
DestByteIndex + AddForNext, NumBytes - 1, AddForNext)
|
|
).
|
|
|
|
% General case. Reduce the number of writes by aligning to the next
|
|
% byte boundary in the destination bitmap so each byte is written once.
|
|
%
|
|
:- func unsafe_copy_unaligned_bits(int, bitmap, bit_index,
|
|
bitmap, bit_index, num_bits) = bitmap.
|
|
% :- mode unsafe_copy_unaligned_bits(in, bitmap_ui, in,
|
|
% bitmap_di, in, in) = bitmap_uo is det.
|
|
:- mode unsafe_copy_unaligned_bits(in, in, in,
|
|
bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
unsafe_copy_unaligned_bits(SameBM, SrcBM, SrcStartBit,
|
|
!.DestBM, DestStartBit, !.NumBits) = !:DestBM :-
|
|
% Grab the odd bits at each end of the block in the destination,
|
|
% leaving a block of aligned bytes to copy.
|
|
DestStartIndex = bit_index_in_byte(DestStartBit),
|
|
DestEndBit = DestStartBit + !.NumBits - 1,
|
|
( if DestStartIndex = 0 then
|
|
NumBitsAtStart = 0,
|
|
StartBits = 0
|
|
else
|
|
NumBitsAtStart = bits_per_byte - DestStartIndex,
|
|
StartBits = SrcBM ^ unsafe_bits(SrcStartBit, NumBitsAtStart),
|
|
!:NumBits = !.NumBits - NumBitsAtStart
|
|
),
|
|
|
|
NewSrcStartBit = (SrcStartBit + NumBitsAtStart),
|
|
NewDestStartBit = (DestStartBit + NumBitsAtStart),
|
|
|
|
DestEndIndex = bit_index_in_byte(DestEndBit),
|
|
( if DestEndIndex = bits_per_byte - 1 then
|
|
NumBitsAtEnd = 0,
|
|
EndBits = 0
|
|
else
|
|
NumBitsAtEnd = DestEndIndex + 1,
|
|
SrcEndBit = NewSrcStartBit + !.NumBits - 1,
|
|
EndBits =
|
|
SrcBM ^ unsafe_bits(SrcEndBit - NumBitsAtEnd + 1, NumBitsAtEnd),
|
|
!:NumBits = !.NumBits - NumBitsAtEnd
|
|
),
|
|
|
|
% Do the block copy.
|
|
NumBytes = !.NumBits `unchecked_quotient` bits_per_byte,
|
|
Direction = choose_copy_direction(SameBM, NewSrcStartBit, NewDestStartBit),
|
|
SrcBitIndex = bit_index_in_byte(NewSrcStartBit),
|
|
|
|
(
|
|
Direction = left_to_right,
|
|
SrcStartByteIndex = byte_index_for_bit(NewSrcStartBit),
|
|
DestStartByteIndex = byte_index_for_bit(NewDestStartBit),
|
|
!:DestBM = unsafe_copy_unaligned_bytes_ltor(SrcBM,
|
|
SrcStartByteIndex + 1, SrcBitIndex,
|
|
SrcBM ^ unsafe_byte(SrcStartByteIndex),
|
|
!.DestBM, DestStartByteIndex, NumBytes)
|
|
;
|
|
Direction = right_to_left,
|
|
SrcStartByteIndex = byte_index_for_bit(NewSrcStartBit + !.NumBits - 1),
|
|
DestStartByteIndex =
|
|
byte_index_for_bit(NewDestStartBit + !.NumBits - 1),
|
|
!:DestBM = unsafe_copy_unaligned_bytes_rtol(SrcBM,
|
|
SrcStartByteIndex - 1, SrcBitIndex,
|
|
SrcBM ^ unsafe_byte(SrcStartByteIndex),
|
|
!.DestBM, DestStartByteIndex, NumBytes)
|
|
),
|
|
|
|
% Fill in the partial bytes at the start and end of the range.
|
|
( if NumBitsAtStart = 0 then
|
|
true
|
|
else
|
|
PartialDestStartByteIndex = byte_index_for_bit(DestStartBit),
|
|
!:DestBM =
|
|
!.DestBM ^ unsafe_byte(PartialDestStartByteIndex) :=
|
|
set_bits_in_byte(
|
|
!.DestBM ^ unsafe_byte(PartialDestStartByteIndex),
|
|
DestStartIndex, NumBitsAtStart, StartBits)
|
|
),
|
|
|
|
( if NumBitsAtEnd = 0 then
|
|
true
|
|
else
|
|
PartialDestEndByteIndex = byte_index_for_bit(DestEndBit),
|
|
!:DestBM =
|
|
!.DestBM ^ unsafe_byte(PartialDestEndByteIndex) :=
|
|
set_bits_in_byte(
|
|
!.DestBM ^ unsafe_byte(PartialDestEndByteIndex),
|
|
0, NumBitsAtEnd, EndBits)
|
|
).
|
|
|
|
:- func unsafe_copy_unaligned_bytes_ltor(bitmap, byte_index, bit_index_in_byte,
|
|
byte, bitmap, byte_index, num_bytes) = bitmap.
|
|
:- mode unsafe_copy_unaligned_bytes_ltor(in, in, in, in,
|
|
bitmap_di, in, in) = bitmap_uo is det.
|
|
% :- mode unsafe_copy_unaligned_bytes_ltor(bitmap_ui, in, in, in,
|
|
% bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
unsafe_copy_unaligned_bytes_ltor(SrcBM, SrcByteIndex, SrcBitIndex,
|
|
PrevSrcByteBits, !.DestBM, DestByteIndex, NumBytes) = !:DestBM :-
|
|
( if NumBytes =< 0 then
|
|
true
|
|
else
|
|
% Combine parts of two adjacent bytes in the source bitmap
|
|
% into one byte of the destination.
|
|
%
|
|
% For example, for the call to `unsafe_copy_unaligned_bytes_ltor'
|
|
% that would result from a call:
|
|
% `unsafe_copy_bits(SrcBM, 1, DestBM, 0, 8)',
|
|
% we construct the first byte in the destination by pasting
|
|
% together the last seven bits of `SrcBM ^ byte(0)'
|
|
% (from PrevSrcByteBits) with the first bit of `SrcBM ^ byte(1)'.
|
|
% SrcBM: |0 1234567|0 1234567|01234567|...
|
|
% | \/ |
|
|
% DestBM: |0123456 7|...
|
|
%
|
|
% PrevSrcByteBits will contain the initial contents of `Src ^ byte(0)'
|
|
% (we can't look it up here because in the general case it may be
|
|
% overwritten by previous recursive calls).
|
|
|
|
SrcByteBits = SrcBM ^ unsafe_byte(SrcByteIndex),
|
|
DestByteBits = (PrevSrcByteBits `unchecked_left_shift` SrcBitIndex)
|
|
\/ (SrcByteBits `unchecked_right_shift`
|
|
(bits_per_byte - SrcBitIndex)),
|
|
!:DestBM = !.DestBM ^ unsafe_byte(DestByteIndex) := DestByteBits,
|
|
|
|
unsafe_copy_unaligned_bytes_ltor(SrcBM, SrcByteIndex + 1, SrcBitIndex,
|
|
SrcByteBits, !.DestBM, DestByteIndex + 1, NumBytes - 1) = !:DestBM
|
|
).
|
|
|
|
:- func unsafe_copy_unaligned_bytes_rtol(bitmap, byte_index, bit_index_in_byte,
|
|
byte, bitmap, byte_index, num_bytes) = bitmap.
|
|
:- mode unsafe_copy_unaligned_bytes_rtol(in, in, in, in,
|
|
bitmap_di, in, in) = bitmap_uo is det.
|
|
% :- mode unsafe_copy_unaligned_bytes_rtol(bitmap_ui, in, in, in,
|
|
% bitmap_di, in, in) = bitmap_uo is det.
|
|
|
|
unsafe_copy_unaligned_bytes_rtol(SrcBM, SrcByteIndex, SrcBitIndex,
|
|
PrevSrcByteBits, !.DestBM, DestByteIndex, NumBytes) = !:DestBM :-
|
|
( if NumBytes =< 0 then
|
|
true
|
|
else
|
|
% Combine parts of two adjacent bytes in the source bitmap
|
|
% into one byte of the destination.
|
|
%
|
|
% For example, for the first call to `unsafe_copy_unaligned_bytes_ltor'
|
|
% resulting from a call `unsafe_copy_bits_in_bitmap(SrcBM, 7, 8, 8)'
|
|
% we construct the second byte in the destination by pasting together
|
|
% the last bit of `SrcBM ^ byte(0)' with the first seven bits of
|
|
% `SrcBM ^ byte(1)' (from PrevSrcByteBits).
|
|
% SrcBM: |0123456 7|0123456 7|01234567|
|
|
% | \/ |
|
|
% DestBM: |01234567|0 1234567|
|
|
%
|
|
% PrevSrcByteBits will contain the initial contents of `Src ^ byte(1)'
|
|
% (we can't look it up here because in the general case it may be
|
|
% overwritten by previous recursive calls).
|
|
|
|
SrcByteBits = SrcBM ^ unsafe_byte(SrcByteIndex),
|
|
DestByteBits = (SrcByteBits `unchecked_left_shift` SrcBitIndex)
|
|
\/ (PrevSrcByteBits `unchecked_right_shift`
|
|
(bits_per_byte - SrcBitIndex)),
|
|
!:DestBM = !.DestBM ^ unsafe_byte(DestByteIndex) := DestByteBits,
|
|
|
|
!:DestBM = unsafe_copy_unaligned_bytes_rtol(SrcBM, SrcByteIndex - 1,
|
|
SrcBitIndex, SrcByteBits, !.DestBM,
|
|
DestByteIndex - 1, NumBytes - 1)
|
|
).
|
|
|
|
:- type copy_direction
|
|
---> left_to_right
|
|
; right_to_left.
|
|
|
|
% choose_copy_direction(SameBM, SrcStartBit, DestStartBit)
|
|
%
|
|
% Choose a direction that will avoid overwriting data
|
|
% before it has been copied.
|
|
% Where it doesn't matter, prefer left_to_right for better performance.
|
|
%
|
|
:- func choose_copy_direction(int, bit_index, bit_index) = copy_direction.
|
|
|
|
choose_copy_direction(SameBM, SrcStartBit, DestStartBit) =
|
|
( if SameBM = 1, SrcStartBit < DestStartBit then
|
|
right_to_left
|
|
else
|
|
left_to_right
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
to_string(BM) = Str :-
|
|
% Note: this should be kept in sync with MR_bitmap_to_string in
|
|
% runtime/mercury_bitmap.c.
|
|
%
|
|
NumBits = BM ^ num_bits,
|
|
to_string_chars(byte_index_for_bit(NumBits - 1), BM, ['>'], BitChars),
|
|
LenChars = to_char_list(int_to_string(NumBits)),
|
|
Chars = ['<' | LenChars] ++ [':' | BitChars],
|
|
Str = string.from_char_list(Chars).
|
|
|
|
:- pred to_string_chars(int, bitmap, list(char), list(char)).
|
|
% :- mode to_string_chars(in, bitmap_ui, in, out) is det.
|
|
:- mode to_string_chars(in, in, in, out) is det.
|
|
|
|
to_string_chars(Index, BM, !Chars) :-
|
|
( if Index < 0 then
|
|
true
|
|
else
|
|
Byte = BM ^ unsafe_byte(Index),
|
|
Mask = n_bit_mask(4),
|
|
( if
|
|
char.int_to_hex_digit((Byte `unchecked_right_shift` 4) /\ Mask,
|
|
HighChar),
|
|
char.int_to_hex_digit(Byte /\ Mask, LowChar)
|
|
then
|
|
!:Chars = [HighChar, LowChar | !.Chars],
|
|
to_string_chars(Index - 1, BM, !Chars)
|
|
else
|
|
throw(software_error("bitmap.to_string: internal error"))
|
|
)
|
|
).
|
|
|
|
from_string(Str) = BM :-
|
|
string.unsafe_index_next(Str, 0, Start, '<'),
|
|
string.unsafe_index(Str, Start, Char),
|
|
char.is_digit(Char),
|
|
string.unsafe_prev_index(Str, length(Str), End, '>'),
|
|
string.sub_string_search_start(Str, ":", Start, Colon),
|
|
SizeStr = string.unsafe_between(Str, Start, Colon),
|
|
string.to_int(SizeStr, Size),
|
|
( if Size >= 0 then
|
|
BM0 = allocate_bitmap(Size),
|
|
string.unsafe_index_next(Str, Colon, AfterColon, _),
|
|
hex_chars_to_bitmap(Str, AfterColon, End, 0, BM0, BM)
|
|
else
|
|
fail
|
|
).
|
|
|
|
:- pred hex_chars_to_bitmap(string::in, int::in, int::in, byte_index::in,
|
|
bitmap::bitmap_di, bitmap::bitmap_uo) is semidet.
|
|
|
|
hex_chars_to_bitmap(Str, Index, End, ByteIndex, !BM) :-
|
|
( if Index = End then
|
|
true
|
|
else if Index + 1 = End then
|
|
% Each byte of the bitmap should have mapped to a pair of characters.
|
|
fail
|
|
else
|
|
char.hex_digit_to_int(Str ^ unsafe_elem(Index), HighNibble),
|
|
char.hex_digit_to_int(Str ^ unsafe_elem(Index + 1), LowNibble),
|
|
Byte = (HighNibble `unchecked_left_shift` 4) \/ LowNibble,
|
|
!:BM = !.BM ^ unsafe_byte(ByteIndex) := Byte,
|
|
hex_chars_to_bitmap(Str, Index + 2, End, ByteIndex + 1, !BM)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
to_byte_string(BM) = string.join_list(".", bitmap_to_byte_strings(BM)).
|
|
|
|
:- func bitmap_to_byte_strings(bitmap) = list(string).
|
|
% :- mode bitmap_to_byte_strings(bitmap_ui) = out is det.
|
|
:- mode bitmap_to_byte_strings(in) = out is det.
|
|
|
|
bitmap_to_byte_strings(BM) = Strs :-
|
|
NumBits = BM ^ num_bits,
|
|
Strs = bitmap_to_byte_strings(BM, NumBits, []).
|
|
|
|
:- func bitmap_to_byte_strings(bitmap, int, list(string)) = list(string).
|
|
% :- mode bitmap_to_byte_strings(bitmap_ui, in, in) = out is det.
|
|
:- mode bitmap_to_byte_strings(in, in, in) = out is det.
|
|
|
|
bitmap_to_byte_strings(BM, NumBits, !.Strs) = !:Strs :-
|
|
( if NumBits =< 0 then
|
|
true
|
|
else
|
|
ThisByte0 = BM ^ unsafe_byte(byte_index_for_bit(NumBits - 1)),
|
|
LastBitIndex = bit_index_in_byte(NumBits - 1),
|
|
( if LastBitIndex = bits_per_byte - 1 then
|
|
BitsThisByte = bits_per_byte,
|
|
ThisByte = ThisByte0
|
|
else
|
|
BitsThisByte = LastBitIndex + 1,
|
|
ThisByte = ThisByte0 `unchecked_right_shift`
|
|
(bits_per_byte - BitsThisByte)
|
|
),
|
|
ThisByteStr = string.pad_left(string.int_to_base_string(ThisByte, 2),
|
|
'0', BitsThisByte),
|
|
!:Strs = [ThisByteStr | !.Strs],
|
|
!:Strs = bitmap_to_byte_strings(BM, NumBits - BitsThisByte, !.Strs)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
hash(BM) = HashVal :-
|
|
% NOTE: bitmap.hash is also defined as MR_hash_bitmap in
|
|
% runtime/mercury_bitmap.h. The two definitions must be kept identical.
|
|
%
|
|
NumBits = BM ^ num_bits,
|
|
NumBytes0 = NumBits `unchecked_quotient` bits_per_byte,
|
|
( if NumBits `unchecked_rem` bits_per_byte = 0 then
|
|
NumBytes = NumBytes0
|
|
else
|
|
NumBytes = NumBytes0 + 1
|
|
),
|
|
hash_2(BM, 0, NumBytes, 0, HashVal0),
|
|
HashVal = HashVal0 `xor` NumBits.
|
|
|
|
:- pred hash_2(bitmap::in, byte_index::in, int::in, int::in, int::out) is det.
|
|
|
|
hash_2(BM, Index, Length, !HashVal) :-
|
|
( if Index < Length then
|
|
combine_hash(BM ^ unsafe_byte(Index), !HashVal),
|
|
hash_2(BM, Index + 1, Length, !HashVal)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred combine_hash(int::in, int::in, int::out) is det.
|
|
|
|
combine_hash(X, H0, H) :-
|
|
H1 = H0 `xor` (H0 << 5),
|
|
H = H1 `xor` X.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% A bitmap is represented in C as a size (in bits) and an array of bytes.
|
|
%
|
|
% NOTE: the `filler' bits in the last element of the array *must* be clear
|
|
% (i.e. zero). This makes the unification, comparison and the set operations
|
|
% simpler to implement.
|
|
%
|
|
|
|
:- pragma foreign_decl("C", "
|
|
#include ""mercury_types.h""
|
|
#include ""mercury_bitmap.h""
|
|
#include ""mercury_type_info.h""
|
|
").
|
|
|
|
:- pragma foreign_decl("Java", "
|
|
import jmercury.runtime.MercuryBitmap;
|
|
").
|
|
|
|
:- pragma foreign_type("C", bitmap, "MR_BitmapPtr",
|
|
[can_pass_as_mercury_type])
|
|
where equality is bitmap_equal, comparison is bitmap_compare.
|
|
:- pragma foreign_type("Java", bitmap, "jmercury.runtime.MercuryBitmap")
|
|
where equality is bitmap_equal, comparison is bitmap_compare.
|
|
:- pragma foreign_type("C#", bitmap, "mercury.runtime.MercuryBitmap")
|
|
where equality is bitmap_equal, comparison is bitmap_compare.
|
|
:- pragma foreign_type("Erlang", bitmap, "")
|
|
where equality is bitmap_equal, comparison is bitmap_compare.
|
|
|
|
:- pred bitmap_equal(bitmap::in, bitmap::in) is semidet.
|
|
:- pragma terminates(bitmap_equal/2).
|
|
|
|
:- pragma foreign_proc("C",
|
|
bitmap_equal(BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
SUCCESS_INDICATOR = MR_bitmap_eq(BM1, BM2);
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
bitmap_equal(BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
SUCCESS_INDICATOR = BM1.Equals(BM2);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
bitmap_equal(BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
SUCCESS_INDICATOR = BM1.equals(BM2);
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
bitmap_equal(BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
SUCCESS_INDICATOR = (BM1 =:= BM2)
|
|
").
|
|
|
|
bitmap_equal(BM1, BM2) :-
|
|
BM1 ^ num_bits = (BM2 ^ num_bits) @ NumBits,
|
|
bytes_equal(0, byte_index_for_bit(NumBits), BM1, BM2).
|
|
|
|
:- pred bytes_equal(byte_index::in, byte_index::in,
|
|
bitmap::in, bitmap::in) is semidet.
|
|
|
|
bytes_equal(Index, MaxIndex, BM1, BM2) :-
|
|
( if Index =< MaxIndex then
|
|
BM1 ^ unsafe_byte(Index) = BM2 ^ unsafe_byte(Index),
|
|
bytes_equal(Index + 1, MaxIndex, BM1, BM2)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred bitmap_compare(comparison_result::uo, bitmap::in, bitmap::in) is det.
|
|
:- pragma terminates(bitmap_compare/3).
|
|
|
|
:- pragma foreign_proc("C",
|
|
bitmap_compare(Result::uo, BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
MR_Integer res;
|
|
res = MR_bitmap_cmp(BM1, BM2);
|
|
Result = ((res < 0) ? MR_COMPARE_LESS
|
|
: (res == 0) ? MR_COMPARE_EQUAL
|
|
: MR_COMPARE_GREATER);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
bitmap_compare(Result::uo, BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail,
|
|
may_not_duplicate],
|
|
"
|
|
if (BM1.num_bits < BM2.num_bits) {
|
|
Result = builtin.COMPARE_LESS;
|
|
} else if (BM1.num_bits > BM2.num_bits) {
|
|
Result = builtin.COMPARE_GREATER;
|
|
} else {
|
|
Result = builtin.COMPARE_EQUAL;
|
|
for (int i = 0; i < BM1.elements.length; i++) {
|
|
// Mask off sign bits.
|
|
int b1 = ((int) BM1.elements[i]) & 0xff;
|
|
int b2 = ((int) BM2.elements[i]) & 0xff;
|
|
if (b1 < b2) {
|
|
Result = builtin.COMPARE_LESS;
|
|
break;
|
|
}
|
|
if (b1 > b2) {
|
|
Result = builtin.COMPARE_GREATER;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
bitmap_compare(Result::uo, BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail,
|
|
may_not_duplicate],
|
|
"
|
|
if (BM1.num_bits < BM2.num_bits) {
|
|
Result = builtin.COMPARE_LESS;
|
|
} else if (BM1.num_bits > BM2.num_bits) {
|
|
Result = builtin.COMPARE_GREATER;
|
|
} else {
|
|
Result = builtin.COMPARE_EQUAL;
|
|
for (int i = 0; i < BM1.elements.Length; i++) {
|
|
// Mask off sign bits.
|
|
int b1 = ((int) BM1.elements[i]) & 0xff;
|
|
int b2 = ((int) BM2.elements[i]) & 0xff;
|
|
if (b1 < b2) {
|
|
Result = builtin.COMPARE_LESS;
|
|
break;
|
|
}
|
|
if (b1 > b2) {
|
|
Result = builtin.COMPARE_GREATER;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
bitmap_compare(Result::uo, BM1::in, BM2::in),
|
|
[will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
|
|
"
|
|
if
|
|
BM1 =:= BM2 ->
|
|
Result = {'='};
|
|
BM1 < BM2 ->
|
|
Result = {'<'};
|
|
true ->
|
|
Result = {'>'}
|
|
end
|
|
").
|
|
|
|
bitmap_compare(Result, BM1, BM2) :-
|
|
NumBits1 = BM1 ^ num_bits,
|
|
NumBits2 = BM2 ^ num_bits,
|
|
compare(Result0, NumBits1, NumBits2),
|
|
(
|
|
Result0 = (=),
|
|
MaxIndex = byte_index_for_bit(NumBits2),
|
|
bytes_compare(Result, 0, MaxIndex, BM1, BM2)
|
|
;
|
|
( Result0 = (<)
|
|
; Result0 = (>)
|
|
),
|
|
Result = Result0
|
|
).
|
|
|
|
:- pred bytes_compare(comparison_result, byte_index, byte_index,
|
|
bitmap, bitmap).
|
|
:- mode bytes_compare(uo, in, in, in, in) is det.
|
|
|
|
bytes_compare(Result, Index, MaxIndex, BM1, BM2) :-
|
|
( if Index =< MaxIndex then
|
|
Byte1 = BM1 ^ unsafe_byte(Index),
|
|
Byte2 = BM2 ^ unsafe_byte(Index),
|
|
compare(Result0, Byte1, Byte2),
|
|
(
|
|
Result0 = (=),
|
|
bytes_compare(Result, Index + 1, MaxIndex, BM1, BM2)
|
|
;
|
|
( Result0 = (<)
|
|
; Result0 = (>)
|
|
),
|
|
Result = Result0
|
|
)
|
|
else
|
|
Result = (=)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func allocate_bitmap(num_bits) = bitmap.
|
|
:- mode allocate_bitmap(in) = bitmap_uo is det.
|
|
|
|
:- pragma foreign_proc("C",
|
|
allocate_bitmap(N::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
MR_allocate_bitmap_msg(BM, N, MR_ALLOC_ID);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
allocate_bitmap(N::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = new MercuryBitmap(N);
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
allocate_bitmap(N::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
BM = new mercury.runtime.MercuryBitmap(N);
|
|
").
|
|
|
|
:- pragma foreign_proc("Erlang",
|
|
allocate_bitmap(N::in) = (BM::bitmap_uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
|
|
"
|
|
Roundup = 8 * ((N + 7) div 8),
|
|
Bin = <<0:Roundup>>,
|
|
BM = {Bin, N}
|
|
").
|
|
|
|
:- func resize_bitmap(bitmap, num_bits) = bitmap.
|
|
:- mode resize_bitmap(bitmap_di, in) = bitmap_uo is det.
|
|
|
|
resize_bitmap(OldBM, N) =
|
|
copy_bits(OldBM, 0, allocate_bitmap(N), 0, int.min(OldBM ^ num_bits, N)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The byte index containing the given bit.
|
|
%
|
|
:- func byte_index_for_bit(bit_index) = byte_index.
|
|
|
|
byte_index_for_bit(I) =
|
|
( if I < 0 then
|
|
-1
|
|
else
|
|
unchecked_quotient(I, bits_per_byte)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type bit_index_in_byte == int.
|
|
|
|
% Convert a bit index for a bitmap into a bit index into a
|
|
% byte in the bitmap.
|
|
%
|
|
:- func bit_index_in_byte(bit_index) = bit_index_in_byte.
|
|
|
|
bit_index_in_byte(I) = I `unchecked_rem` bits_per_byte.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Construct the bitmask for a given bit in a byte. Bits are numbered
|
|
% from most significant to least significant (starting at zero).
|
|
%
|
|
% E.g. assuming bits_per_byte = 8 and I = 3 then
|
|
% bitmask(I) = 2'00010000
|
|
%
|
|
:- func bitmask(bit_index_in_byte) = byte.
|
|
|
|
bitmask(I) = 1 `unchecked_left_shift`
|
|
(bits_per_byte - 1 - bit_index_in_byte(I)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Construct a bitmask containing the N least significant bits set.
|
|
%
|
|
% E.g. assuming bits_per_byte = 8 and I = 4 then
|
|
% n_bit_mask(I) = 2'00001111
|
|
%
|
|
:- func n_bit_mask(num_bits) = byte.
|
|
|
|
n_bit_mask(N) = BitsMask :-
|
|
BitMask = 1 `unchecked_left_shift` (N - 1),
|
|
BitsMask = BitMask \/ (BitMask - 1).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% extract_bits_from_byte(Byte, FirstBit, NumBits)
|
|
% Return an integer whose NumBits least significant bits contain
|
|
% bits FirstBit, FirstBit + 1, ... FirstBit + NumBits - 1,
|
|
% in order from most significant to least significant.
|
|
%
|
|
:- func extract_bits_from_byte(byte, bit_index_in_byte, num_bits) = byte.
|
|
|
|
extract_bits_from_byte(Byte, FirstBit, NumBits) = Bits :-
|
|
% Shift the last bit in the selected bit range
|
|
% to the least significant position.
|
|
LastBit = FirstBit + NumBits - 1,
|
|
Shift = bits_per_byte - 1 - LastBit,
|
|
Bits = (Byte `unchecked_right_shift` Shift) /\ n_bit_mask(NumBits).
|
|
|
|
% set_bits_in_byte(Byte, FirstBit, NumBits, Bits)
|
|
%
|
|
% Replace bits FirstBit, FirstBit + 1, ... FirstBit + NumBits - 1,
|
|
% with the NumBits least significant bits of Bits, replacing FirstBit
|
|
% with the most significant of those bits.
|
|
%
|
|
:- func set_bits_in_byte(byte, bit_index_in_byte, num_bits, byte) = byte.
|
|
|
|
set_bits_in_byte(Byte0, FirstBit, NumBits, Bits) = Byte :-
|
|
LastBit = FirstBit + NumBits - 1,
|
|
Shift = bits_per_byte - 1 - LastBit,
|
|
Mask = n_bit_mask(NumBits),
|
|
BitsToSet = Mask /\ Bits,
|
|
Byte = (Byte0 /\ \ (Mask `unchecked_left_shift` Shift))
|
|
\/ (BitsToSet `unchecked_left_shift` Shift).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% throw_bounds_error(BM, PredName, Index)
|
|
%
|
|
:- func throw_bounds_error(bitmap, string, bit_index) = _ is erroneous.
|
|
|
|
throw_bounds_error(BM, Pred, Index) =
|
|
throw_bitmap_error(
|
|
% XXX Why is the bounds "[x - y)" in throw_bitmap_error/3
|
|
% versus "[x, y)" in throw_bounds_error/4?
|
|
string.format(
|
|
"%s: index %d is out of bounds [0 - %d).",
|
|
[s(Pred), i(Index), i(BM ^ num_bits)])).
|
|
|
|
% throw_bounds_error(BM, PredName, Index, NumBits)
|
|
%
|
|
:- func throw_bounds_error(bitmap, string, bit_index, num_bits) = _
|
|
is erroneous.
|
|
|
|
throw_bounds_error(BM, Pred, Index, NumBits) = _ :-
|
|
throw_bounds_error(BM, Pred, Index, NumBits).
|
|
|
|
throw_bounds_error(BM, Pred, Index, NumBits) :-
|
|
( if NumBits < 0 then
|
|
string.format("%s: negative number of bits: %d.",
|
|
[s(Pred), i(NumBits)], Msg)
|
|
else
|
|
% XXX Why is the bounds "[x - y)" in throw_bitmap_error/3
|
|
% versus "[x, y)" in throw_bounds_error/4?
|
|
string.format(
|
|
"%s: %d bits starting at bit %d is out of bounds [0, %d).",
|
|
[s(Pred), i(NumBits), i(Index), i(BM ^ num_bits)], Msg)
|
|
),
|
|
throw_bitmap_error(Msg).
|
|
|
|
:- func throw_bitmap_error(string) = _ is erroneous.
|
|
|
|
throw_bitmap_error(Msg) = _ :-
|
|
throw_bitmap_error(Msg).
|
|
|
|
:- pred throw_bitmap_error(string::in) is erroneous.
|
|
|
|
throw_bitmap_error(Msg) :-
|
|
throw(bitmap_error(Msg)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|