mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 02:43:40 +00:00
compiler/simplify_goal_call.m:
If the --warn-suspicious-recursion option is set, and if the warning
isn't disabled, generate warnings for two different kinds of suspicious
recursion. They are both related to, but separate from, the warning
we have long generated for infinite recursion, which occurs when
the input args of a recursive call are the same as the corresponding
args in the clause head.
Both kinds suspicious recursion look at the input args of a recursive call
that are NOT the same as the corresponding args in the clause head.
Both require these args to have non-unique modes. (If they have unique
modes, then the depth of the recursion may be controlled by state outside
the view of the Mercury compiler, which means that a warning would be
likely to be misleading.)
The first kind is when all these args use state variable notation.
Most of the time, we use state var notation to denote the data structures
updated by the recursive code; having variables using such notation
*controlling* the recursion is much less common, and much more likely
to be unintended. The motivation for the new option was this infinitely
looping code, which resulted from neglecting to s/[X | Xs]/Xs/ after
cutting-and-pasting the clause head to the recursive call.
p([X | Xs], !S) :-
...,
p([X | Xs], !S).
The other kind of suspicious recursive call we warn about involve
input arguments where the base names of the input arguments (the part
before any numeric suffixes) seem be switched between the clause head
and the recursive call, as here:
q(As0, Bs0, ...) :-
...,
q(Bs1, As, ...).
compiler/mercury_compile_main.m:
Disable style warnings when invoked with --make-optimization-interface
or its transitive variant. Without this, warnings about suspicious
recursion would get reported in such invocations.
Move a test from a callee to a caller to allow the callee to be
less indented.
compiler/options.m:
Export functionality to mercury_compile_main.m to make the above possible.
library/string.m:
Add a predicate to convert a string to *reverse* char list directly.
Note a discrepancy between the documentation and the implementation
of the old predicate the new one is based on (which converts a string
to a forward char list).
NEWS:
Note the new predicate in string.m.
compiler/cse_detection.m:
compiler/ctgc.selector.m:
compiler/dead_proc_elim.m:
compiler/deforest.m:
compiler/dep_par_conj.m:
compiler/det_analysis.m:
compiler/distance_granularity.m:
compiler/frameopt.m:
compiler/inst_util.m:
compiler/lp_rational.m:
compiler/matching.m:
compiler/modes.m:
compiler/old_type_constraints.m:
compiler/rbmm.points_to_analysis.m:
compiler/rbmm.region_arguments.m:
compiler/recompilation.usage.m:
compiler/stratify.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/structure_reuse.indirect.m:
compiler/typeclasses.m:
compiler/use_local_vars.m:
deep_profiler/callgraph.m:
deep_profiler/canonical.m:
library/bit_buffer.read.m:
library/bit_buffer.write.m:
library/calendar.m:
library/diet.m:
library/lexer.m:
library/parser.m:
library/parsing_utils.m:
library/ranges.m:
library/set_ctree234.m:
library/set_tree234.m:
library/string.parse_util.m:
library/tree234.m:
library/varset.m:
mdbcomp/program_representation.m:
mdbcomp/rtti_access.m:
profiler/demangle.m:
Avoid warnings for suspicious recursion. In most cases, do this by
wrapping disable_warning scopes around the affected recursive calls;
in a few cases, do this by changing the code.
tests/warnings/suspicious_recursion.{m,exp}:
A test case for the new warnings.
tests/warnings/Mercury.options:
tests/warnings/Mmakefile:
Enable the new test case.
666 lines
24 KiB
Mathematica
666 lines
24 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2007, 2010-2011 The University of Melbourne
|
|
% Copyright (C) 2014-2015, 2018 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
% File: bit_buffer.read.m.
|
|
% Main author: stayl.
|
|
% Stability: low.
|
|
%
|
|
% A bit buffer provides an interface between bit-oriented input requests
|
|
% and byte-oriented streams, getting a large chunk of bits with one call
|
|
% to `bulk_get', then satisfying bit-oriented requests from the buffer.
|
|
%
|
|
% Return values of `error(...)' are only used for errors in the stream
|
|
% being read. Once an error value has been returned, all future calls
|
|
% will return that error.
|
|
%
|
|
% Bounds errors or invalid argument errors (for example a read request
|
|
% for a negative number of bits) will result in an exception being thrown.
|
|
% Requests triggering an exception in this way will not change the state
|
|
% of the stream.
|
|
%
|
|
% 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 bit_buffer.read.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
:- import_module bitmap.
|
|
|
|
:- type read_buffer(Stream, State, Error).
|
|
% <= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
|
|
:- type read_buffer ==
|
|
read_buffer(error_stream, error_state, error_stream_error).
|
|
|
|
:- type io_read_buffer ==
|
|
read_buffer(io.binary_input_stream, io.state, io.error).
|
|
|
|
:- inst uniq_read_buffer == ground. % XXX Should be unique.
|
|
:- mode read_buffer_di == in(uniq_read_buffer).
|
|
:- mode read_buffer_ui == in(uniq_read_buffer).
|
|
:- mode read_buffer_uo == out(uniq_read_buffer).
|
|
|
|
% new(NumBytes, Stream, State) creates a buffer which will read from
|
|
% the stream specified by Stream and State in chunks of NumBytes bytes.
|
|
% `NumBytes' must at least the size of a Mercury int, given by
|
|
% int.bits_per_int. If it is less, the size of an int will be used
|
|
% instead.
|
|
%
|
|
:- func new(num_bytes, Stream, State) = read_buffer(Stream, State, Error)
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
:- mode new(in, in, di) = read_buffer_uo is det.
|
|
|
|
% new(BitIndex, StartIndex, NumBits)
|
|
% Create a buffer which reads bits from a bitmap, not from a stream.
|
|
%
|
|
:- func new_bitmap_reader(bitmap, bit_index, num_bits) = read_buffer.
|
|
:- mode new_bitmap_reader(in, in, in) = read_buffer_uo is det.
|
|
|
|
:- func new_bitmap_reader(bitmap) = read_buffer.
|
|
:- mode new_bitmap_reader(in) = read_buffer_uo is det.
|
|
|
|
% How many bits to be read does the buffer contain.
|
|
%
|
|
:- func num_buffered_bits(read_buffer(_, _, _)) = num_bits.
|
|
:- mode num_buffered_bits(read_buffer_ui) = out is det.
|
|
|
|
% How many bits need to be read to get to the next byte boundary.
|
|
%
|
|
:- func num_bits_to_byte_boundary(read_buffer(_, _, _)) = num_bits.
|
|
:- mode num_bits_to_byte_boundary(read_buffer_ui) = out is det.
|
|
|
|
% Find out whether there are bits left in the stream or an error
|
|
% has been found.
|
|
%
|
|
:- pred buffer_status(stream.result(Error),
|
|
read_buffer(Stream, State, Error),
|
|
read_buffer(Stream, State, Error))
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
:- mode buffer_status(out, read_buffer_di, read_buffer_uo) is det.
|
|
|
|
% Read a bit from the buffer.
|
|
%
|
|
% This implements the get/4 method of class stream.reader.
|
|
%
|
|
:- pred get_bit(stream.result(bool, Error), read_buffer(Stream, State, Error),
|
|
read_buffer(Stream, State, Error))
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
:- mode get_bit(out, read_buffer_di, read_buffer_uo) is det.
|
|
|
|
% get_bits(Index, NumBits, !Word, NumBitsRead, Result, !Buffer).
|
|
%
|
|
% Read NumBits bits from the buffer into a word starting at Index,
|
|
% where the highest order bit is bit zero.
|
|
% 0 =< NumBits =< int.bits_per_int.
|
|
%
|
|
% This implements the bulk_get/9 method of stream.bulk_reader.
|
|
%
|
|
% To read into the lower order bits of the word, use
|
|
% `get_bits(bits_per_int - NumBits, NumBits, ...)'.
|
|
%
|
|
:- pred get_bits(bit_index, num_bits, word, word, num_bits,
|
|
stream.res(Error), read_buffer(Stream, State, Error),
|
|
read_buffer(Stream, State, Error))
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
:- mode get_bits(in, in, di, uo, out, out,
|
|
read_buffer_di, read_buffer_uo) is det.
|
|
|
|
% get_bitmap(!Bitmap, NumBitsRead, Result, !Buffer)
|
|
%
|
|
% Fill a bitmap from the buffered stream, returning the number
|
|
% of bits read.
|
|
%
|
|
% Note that this is much more efficient if the initial position in
|
|
% the buffer is at a byte boundary (for example after a call to
|
|
% skip_padding_to_byte).
|
|
%
|
|
:- pred get_bitmap(bitmap, bitmap, num_bits,
|
|
stream.res(Error), read_buffer(Stream, State, Error),
|
|
read_buffer(Stream, State, Error))
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
:- mode get_bitmap(bitmap_di, bitmap_uo, out, out,
|
|
read_buffer_di, read_buffer_uo) is det.
|
|
|
|
% get_bitmap(Index, NumBits, !Bitmap, NumBitsRead, Result, !Buffer)
|
|
%
|
|
% Note that this is much more efficient if both Index and the initial
|
|
% position in the buffer are both at a byte boundary (for example after
|
|
% a call to skip_padding_to_byte).
|
|
%
|
|
% This implements the bulk_get method of stream.bulk_reader.
|
|
%
|
|
:- pred get_bitmap(bit_index, num_bits, bitmap, bitmap, num_bits,
|
|
stream.res(Error), read_buffer(Stream, State, Error),
|
|
read_buffer(Stream, State, Error))
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
:- mode get_bitmap(in, in, bitmap_di, bitmap_uo, out, out,
|
|
read_buffer_di, read_buffer_uo) is det.
|
|
|
|
% finalize(Buffer, Stream, State, BufferBM,
|
|
% IndexInBufferBM, NumBitsInBufferBM)
|
|
%
|
|
% Returns the stream, state and the unread buffered bits.
|
|
%
|
|
:- pred finalize(read_buffer(Stream, State, Error), Stream, State,
|
|
bitmap, bit_index, num_bits)
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
:- mode finalize(read_buffer_di, out, uo, bitmap_uo, out, out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- implementation.
|
|
|
|
/*
|
|
** None of these instances work because of limitations in the type and
|
|
** RTTI system.
|
|
**
|
|
|
|
:- interface.
|
|
|
|
%
|
|
% A bit buffer is a stream of bits.
|
|
%
|
|
|
|
:- type read_stream(Stream)
|
|
---> read_stream.
|
|
|
|
:- instance stream.stream(read_stream(Stream, Error),
|
|
read_buffer(Stream, State, Error))
|
|
<= stream.bulk_reader(Stream, bit_index, bitmap, State, Error).
|
|
|
|
:- instance stream.input(read_stream(Stream, Error),
|
|
read_buffer(Stream, State, Error))
|
|
<= stream.input(Stream, State).
|
|
|
|
:- instance stream.reader(read_stream(Stream, Error), bool,
|
|
read_buffer(Stream, State, Error), Error)
|
|
<= stream.bulk_reader(Stream, bit_index, bitmap, State, Error).
|
|
|
|
:- instance stream.bulk_reader(read_stream(Stream, Error),
|
|
bit_index, word, read_buffer(Stream, State, Error), Error)
|
|
<= stream.bulk_reader(Stream, bit_index, bitmap, State, Error).
|
|
|
|
:- instance stream.bulk_reader(read_stream(Stream, Error),
|
|
bit_index, bitmap, read_buffer(Stream, State, Error), Error)
|
|
<= stream.bulk_reader(Stream, bit_index, bitmap, State, Error).
|
|
*/
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- implementation.
|
|
|
|
:- import_module require.
|
|
|
|
% If an operation reports an error, it must set the read_error
|
|
% field so that all further operations report an error as well.
|
|
% This is done at the site the error is discovered; places which
|
|
% just convert the type of an error value don't need to set the
|
|
% read_error field.
|
|
%
|
|
:- type read_buffer(Stream, State, Error)
|
|
---> read_buffer(bit_buffer :: bit_buffer(Stream, State, Error)).
|
|
% <= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
|
|
new(NumBytes, Stream, State) = Buffer :-
|
|
% We store Size + bits_per_int bits in the buffer. The first word
|
|
% of the buffer contains the bits that were in the buffer when it
|
|
% was last refilled.
|
|
%
|
|
% We require the buffer size to be at least bits_per_int so that
|
|
% a call to `get_bits' can always be satisfied with a single buffer refill.
|
|
% Allowing smaller buffer sizes would complicate the code for a case
|
|
% that shouldn't occur in practice anyway.
|
|
%
|
|
SizeInBits = NumBytes * bits_per_byte,
|
|
ChunkSize = int.max(SizeInBits, bits_per_int),
|
|
BMSize = ChunkSize + bits_per_int,
|
|
BM = bitmap.init(BMSize, no),
|
|
|
|
% Start at the end of the buffer to force a fill on the first read.
|
|
%
|
|
Pos = BMSize,
|
|
Buffer = read_buffer(new_buffer(BM, Pos, BMSize, yes, Stream, State)).
|
|
|
|
new_bitmap_reader(BM, StartIndex, NumBits) = Buffer :-
|
|
Buffer = read_buffer(new_buffer(BM, StartIndex, NumBits, no,
|
|
error_stream, error_state)).
|
|
|
|
new_bitmap_reader(BM) = new_bitmap_reader(BM, 0, BM ^ num_bits).
|
|
|
|
num_buffered_bits(Buffer) =
|
|
% The computed number of bits may be negative if there has been an error.
|
|
int.max(Buffer ^ bit_buffer ^ size - Buffer ^ bit_buffer ^ pos, 0).
|
|
|
|
num_bits_to_byte_boundary(Buffer) = NumBits :-
|
|
Pos = Buffer ^ bit_buffer ^ pos,
|
|
PosInByte = Pos `unchecked_rem` bits_per_byte,
|
|
( if PosInByte = 0 then
|
|
NumBits = 0
|
|
else
|
|
NumBits = bits_per_byte - PosInByte
|
|
).
|
|
|
|
buffer_status(Result, !Buffer) :-
|
|
Status = !.Buffer ^ bit_buffer ^ read_status,
|
|
(
|
|
Status = ok,
|
|
NumBufferedBits = !.Buffer ^ num_buffered_bits,
|
|
( if NumBufferedBits > 0 then
|
|
Result = ok
|
|
else
|
|
refill_read_buffer(RefillResult, !Buffer),
|
|
(
|
|
RefillResult = ok,
|
|
NewNumBufferedBits = !.Buffer ^ num_buffered_bits,
|
|
( if NewNumBufferedBits > 0 then
|
|
Result = ok
|
|
else
|
|
Result = eof
|
|
)
|
|
;
|
|
RefillResult = error(Err),
|
|
Result = error(Err)
|
|
)
|
|
)
|
|
;
|
|
Status = error(Err),
|
|
Result = error(Err)
|
|
).
|
|
|
|
get_bit(BitResult, !Buffer) :-
|
|
get_bits(0, 1, 0, Word, NumBitsRead, BitsResult, !Buffer),
|
|
(
|
|
BitsResult = ok,
|
|
( if NumBitsRead = 1 then
|
|
BitResult = ok(if Word = 0 then no else yes)
|
|
else
|
|
BitResult = eof
|
|
)
|
|
;
|
|
BitsResult = error(Error),
|
|
BitResult = error(Error)
|
|
).
|
|
|
|
get_bits(Index, NumBits, !.Word, unsafe_promise_unique(!:Word),
|
|
NumBitsRead, BitsResult, !Buffer) :-
|
|
Status = !.Buffer ^ bit_buffer ^ read_status,
|
|
(
|
|
Status = ok,
|
|
( if NumBits > 0 then
|
|
( if NumBits > bits_per_int then
|
|
unexpected($pred, "invalid number of bits")
|
|
else
|
|
true
|
|
),
|
|
( if !.Buffer ^ num_buffered_bits >= NumBits then
|
|
BitsResult = ok,
|
|
do_get_bits(Index, NumBits, !Word, NumBitsRead, !Buffer)
|
|
else
|
|
refill_read_buffer(RefillResult, !Buffer),
|
|
(
|
|
RefillResult = ok,
|
|
BitsResult = ok,
|
|
do_get_bits(Index, NumBits, !Word, NumBitsRead, !Buffer)
|
|
;
|
|
RefillResult = error(Err),
|
|
NumBitsRead = 0,
|
|
BitsResult = error(Err)
|
|
)
|
|
)
|
|
else if NumBits = 0 then
|
|
NumBitsRead = 0,
|
|
BitsResult = ok
|
|
else
|
|
unexpected($pred, "negative number of bits")
|
|
)
|
|
;
|
|
Status = error(Err),
|
|
NumBitsRead = 0,
|
|
BitsResult = error(Err)
|
|
).
|
|
|
|
:- pred do_get_bits(bit_index::in, num_bits::in, word::in, word::out,
|
|
num_bits::out, read_buffer(Stream, State, Error)::read_buffer_di,
|
|
read_buffer(Stream, State, Error)::read_buffer_uo) is det
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
|
|
do_get_bits(Index, NumBits, !Word, NumBitsRead,
|
|
RB @ read_buffer(!.Buffer), read_buffer(!:Buffer)) :-
|
|
NumBitsAvailable = RB ^ num_buffered_bits,
|
|
Pos = !.Buffer ^ pos,
|
|
NumBitsRead = int.min(NumBitsAvailable, NumBits),
|
|
Bits0 = !.Buffer ^ bitmap ^ bits(Pos, NumBitsRead),
|
|
Bits = Bits0 `unchecked_left_shift` (NumBits - NumBitsRead),
|
|
|
|
LastBit = Index + NumBitsRead - 1,
|
|
Shift = bits_per_int - 1 - LastBit,
|
|
BitMask = 1 `unchecked_left_shift` (NumBits - 1),
|
|
BitsMask = BitMask \/ (BitMask - 1),
|
|
!:Word = !.Word /\ \ (BitsMask `unchecked_left_shift` Shift),
|
|
!:Word = !.Word \/ (Bits `unchecked_left_shift` Shift),
|
|
|
|
set_bitmap(!.Buffer ^ bitmap, Pos + NumBits, !Buffer).
|
|
|
|
get_bitmap(!BM, NumBitsRead, Result, !Buffer) :-
|
|
get_bitmap(0, !.BM ^ num_bits, !BM, NumBitsRead, Result, !Buffer).
|
|
|
|
get_bitmap(Index, NumBits, !BM, NumBitsRead, Result,
|
|
read_buffer(!.Buffer), read_buffer(!:Buffer)) :-
|
|
Status = !.Buffer ^ read_status,
|
|
(
|
|
Status = ok,
|
|
( if
|
|
NumBits > 0,
|
|
in_range(!.BM, Index),
|
|
in_range(!.BM, Index + NumBits - 1)
|
|
then
|
|
UseStream = !.Buffer ^ use_stream,
|
|
(
|
|
UseStream = yes,
|
|
recursively_get_bitmap(Index, NumBits, !BM, 0,
|
|
NumBitsRead, Result, !Buffer)
|
|
;
|
|
UseStream = no,
|
|
Pos = !.Buffer ^ pos,
|
|
Size = !.Buffer ^ size,
|
|
NumBitsRead = min(Size - Pos, NumBits),
|
|
!:BM = copy_bits(!.Buffer ^ bitmap, Pos, !.BM,
|
|
Index, NumBitsRead),
|
|
set_bitmap(!.Buffer ^ bitmap, Pos + NumBits, !Buffer),
|
|
Result = ok
|
|
)
|
|
else if
|
|
NumBits = 0,
|
|
( in_range(!.BM, Index)
|
|
; Index = 0
|
|
)
|
|
then
|
|
NumBitsRead = 0,
|
|
Result = ok
|
|
else
|
|
bitmap.throw_bounds_error(!.BM, "bit_buffer.read.get_bitmap",
|
|
Index, NumBits)
|
|
)
|
|
;
|
|
Status = error(Error),
|
|
NumBitsRead = 0,
|
|
Result = error(Error)
|
|
).
|
|
|
|
:- pred recursively_get_bitmap(bit_index::in, num_bits::in,
|
|
bitmap::bitmap_di, bitmap::bitmap_uo,
|
|
num_bits::in, num_bits::out, stream.res(Error)::out,
|
|
bit_buffer(Stream, State, Error)::bit_buffer_di,
|
|
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
|
|
recursively_get_bitmap(!.Index, !.NumBits, !BM, !NumBitsRead,
|
|
Result, !Buffer) :-
|
|
( if !.NumBits = 0 then
|
|
Result = ok
|
|
else
|
|
% Take the bits that are already in the buffer.
|
|
copy_buffered_bits_to_bitmap(!Index, !NumBits, !BM,
|
|
!NumBitsRead, !Buffer),
|
|
( if
|
|
!.NumBits = 0
|
|
then
|
|
Result = ok
|
|
else if
|
|
!.Index `unchecked_rem` bits_per_byte = 0
|
|
then
|
|
% We can do a bulk_get straight into the result bitmap.
|
|
bulk_get_into_result_bitmap(!Index, !NumBits, !BM, !NumBitsRead,
|
|
BulkGetResult, !Buffer),
|
|
(
|
|
BulkGetResult = ok,
|
|
( if !.NumBits > 0 then
|
|
!:Buffer = read_buffer(!.Buffer),
|
|
get_bits(bits_per_int - !.NumBits, !.NumBits,
|
|
0, LastBits, NumLastBitsRead, LastBitsResult, !Buffer),
|
|
!:Buffer = !.Buffer ^ bit_buffer,
|
|
(
|
|
LastBitsResult = ok,
|
|
|
|
% !.NumBits is correct here, if we didn't read
|
|
% enough bits this will just fill the rest of the
|
|
% range with zero bits.
|
|
!:BM = !.BM ^ bits(!.Index, !.NumBits) := LastBits,
|
|
Result = ok
|
|
;
|
|
LastBitsResult = error(Err),
|
|
Result = error(Err)
|
|
),
|
|
!:NumBitsRead = !.NumBitsRead + NumLastBitsRead
|
|
else
|
|
Result = ok
|
|
)
|
|
;
|
|
BulkGetResult = error(Err),
|
|
Result = error(Err)
|
|
)
|
|
else
|
|
do_refill_read_buffer(RefillRes, !Buffer),
|
|
(
|
|
RefillRes = ok,
|
|
( if read_buffer(!.Buffer) ^ num_buffered_bits > 0 then
|
|
disable_warning [suspicious_recursion] (
|
|
recursively_get_bitmap(!.Index, !.NumBits, !BM,
|
|
!NumBitsRead, Result, !Buffer)
|
|
)
|
|
else
|
|
Result = ok
|
|
)
|
|
;
|
|
RefillRes = error(Err),
|
|
Result = error(Err)
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred copy_buffered_bits_to_bitmap(bit_index::in, bit_index::out,
|
|
num_bits::in, num_bits::out, bitmap::bitmap_di, bitmap::bitmap_uo,
|
|
num_bits::in, num_bits::out, bit_buffer(S, St, E)::bit_buffer_di,
|
|
bit_buffer(S, St, E)::bit_buffer_uo) is det.
|
|
|
|
copy_buffered_bits_to_bitmap(!Index, !NumBits, !BM, !NumBitsRead, !Buffer) :-
|
|
NumBufferedBits = read_buffer(!.Buffer) ^ num_buffered_bits,
|
|
NumBitsToGet = int.min(!.NumBits, NumBufferedBits),
|
|
Pos0 = !.Buffer ^ pos,
|
|
!:BM = copy_bits(!.Buffer ^ bitmap, Pos0, !.BM, !.Index, NumBitsToGet),
|
|
Pos = Pos0 + NumBitsToGet,
|
|
set_bitmap(!.Buffer ^ bitmap, Pos, !Buffer),
|
|
!:Index = !.Index + NumBitsToGet,
|
|
!:NumBits = !.NumBits - NumBitsToGet,
|
|
!:NumBitsRead = !.NumBitsRead + NumBitsToGet.
|
|
|
|
:- pred bulk_get_into_result_bitmap(bit_index::in, bit_index::out,
|
|
num_bits::in, num_bits::out, bitmap::bitmap_di, bitmap::bitmap_uo,
|
|
num_bits::in, num_bits::out, stream.res(E)::out,
|
|
bit_buffer(S, St, E)::bit_buffer_di,
|
|
bit_buffer(S, St, E)::bit_buffer_uo) is det
|
|
<= stream.bulk_reader(S, byte_index, bitmap, St, E).
|
|
|
|
bulk_get_into_result_bitmap(!Index, !NumBits, !BM, !NumBitsRead,
|
|
Result, !Buffer) :-
|
|
StartByteIndex = !.Index `unchecked_quotient` bits_per_byte,
|
|
NumBytesToBulkGet = !.NumBits `unchecked_quotient` bits_per_byte,
|
|
Stream = !.Buffer ^ stream,
|
|
State0 = !.Buffer ^ state,
|
|
stream.bulk_get(Stream, StartByteIndex, NumBytesToBulkGet, !BM,
|
|
NumBytesRead, Result, State0, State),
|
|
(
|
|
Result = ok
|
|
;
|
|
Result = error(_),
|
|
do_set_buffer_error(Result, !Buffer)
|
|
),
|
|
NumBitsBulkRead = NumBytesRead * bits_per_byte,
|
|
!:Index = !.Index + NumBitsBulkRead,
|
|
!:NumBitsRead = !.NumBitsRead + NumBitsBulkRead,
|
|
!:NumBits = !.NumBits - NumBitsBulkRead,
|
|
set_state(State, !Buffer).
|
|
|
|
% This predicate may only be called when the number of buffered bits
|
|
% is less than bits_per_int.
|
|
%
|
|
:- pred refill_read_buffer(stream.res(Error)::out,
|
|
read_buffer(Stream, State, Error)::read_buffer_di,
|
|
read_buffer(Stream, State, Error)::read_buffer_uo) is det
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
|
|
refill_read_buffer(Result, read_buffer(!.Buffer), read_buffer(!:Buffer)) :-
|
|
do_refill_read_buffer(Result, !Buffer).
|
|
|
|
:- pred do_refill_read_buffer(stream.res(Error)::out,
|
|
bit_buffer(Stream, State, Error)::bit_buffer_di,
|
|
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det
|
|
<= stream.bulk_reader(Stream, byte_index, bitmap, State, Error).
|
|
|
|
do_refill_read_buffer(Result, !.Buffer, !:Buffer) :-
|
|
UseStream = !.Buffer ^ use_stream,
|
|
(
|
|
UseStream = yes,
|
|
( if read_buffer(!.Buffer) ^ num_buffered_bits =< bits_per_int then
|
|
true
|
|
else
|
|
unexpected($pred, "too many bits in buffer")
|
|
),
|
|
some [!BM, !State, !Pos, !Size] (
|
|
|
|
!:BM = !.Buffer ^ bitmap,
|
|
!:Pos = !.Buffer ^ pos,
|
|
!:Size = !.Buffer ^ size,
|
|
!:State = !.Buffer ^ state,
|
|
|
|
% Copy the remaining bits back to the first word of the buffer.
|
|
%
|
|
Remain = !.Size - !.Pos,
|
|
OldPos = !.Pos,
|
|
!:Pos = bits_per_int - Remain,
|
|
( if Remain > 0 then
|
|
!:BM = !.BM ^ bits(!.Pos, Remain) :=
|
|
!.BM ^ bits(OldPos, Remain)
|
|
else
|
|
true
|
|
),
|
|
|
|
% Perform a bulk get from the stream into the buffer
|
|
% starting at the second word. bit_buffer.read.new
|
|
% guarantees that !.Size is at least as big as bits_per_int.
|
|
%
|
|
ChunkSize = !.Size - bits_per_int,
|
|
StartByteIndex = bits_per_int `unchecked_quotient` bits_per_byte,
|
|
NumBytesToRead = ChunkSize `unchecked_quotient` bits_per_byte,
|
|
Stream = !.Buffer ^ stream,
|
|
stream.bulk_get(Stream, StartByteIndex, NumBytesToRead, !BM,
|
|
NumBytesRead, Result, !State),
|
|
|
|
% Record the new size of the buffer if `bulk_get' hit eof
|
|
% or an error. Further attempts to refill the buffer will
|
|
% do nothing.
|
|
%
|
|
( if NumBytesRead = NumBytesToRead then
|
|
true
|
|
else
|
|
% XXX We should probably allow the user to attempt to reset
|
|
% the error flag and try again if an error was transient, but
|
|
% the current stream interface doesn't allow for that.
|
|
% If that was allowed we shouldn't modify the size of the
|
|
% buffer or change it to bitmap only here.
|
|
%
|
|
!:Size = NumBytesRead * bits_per_byte + bits_per_int,
|
|
set_use_stream(no, !Buffer)
|
|
),
|
|
set_all(!.BM, !.Pos, !.Size, !.State, [], !Buffer),
|
|
(
|
|
Result = ok
|
|
;
|
|
Result = error(_),
|
|
do_set_buffer_error(Result, !Buffer)
|
|
)
|
|
)
|
|
;
|
|
UseStream = no,
|
|
Result = ok
|
|
).
|
|
|
|
finalize(ReadBuffer @ read_buffer(Buffer), Buffer ^ stream, Buffer ^ state,
|
|
Buffer ^ bitmap, Buffer ^ pos, ReadBuffer ^ num_buffered_bits).
|
|
|
|
% We didn't have enough bits to satisfy a request, so move the position
|
|
% to the end of the buffer.
|
|
%
|
|
:- pred do_set_buffer_error(stream.res(Error)::in,
|
|
bit_buffer(Stream, State, Error)::bit_buffer_di,
|
|
bit_buffer(Stream, State, Error)::bit_buffer_uo) is det.
|
|
|
|
do_set_buffer_error(Error, !Buffer) :-
|
|
set_read_status(Error, !Buffer).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% None of these instances work because of limitations in the type and
|
|
% RTTI system.
|
|
%
|
|
% :- instance stream.stream(read_stream(Stream, Error),
|
|
% read_buffer(Stream, State, Error))
|
|
% <= stream.bulk_reader(Stream, bit_index, bitmap, State, Error)
|
|
% where
|
|
% [
|
|
% (name(_, Name, !Buffer) :-
|
|
% name(!.Buffer ^ read_stream, StreamName,
|
|
% !.Buffer ^ read_buffer_state, State),
|
|
% Name = "bit_buffer.read.read_buffer(" ++ StreamName ++ ")",
|
|
% set_state(State, !Buffer),
|
|
% !:Buffer = unsafe_promise_unique(!.Buffer)
|
|
% )
|
|
% ].
|
|
%
|
|
% :- instance stream.input(read_stream(Stream, Error),
|
|
% read_buffer(Stream, State, Error))
|
|
% <= stream.bulk_reader(Stream, bit_index, bitmap, State, Error)
|
|
% where [].
|
|
%
|
|
% :- instance stream.reader(read_stream(Stream, Error), bool,
|
|
% read_buffer(Stream, State, Error), Error)
|
|
% <= stream.bulk_reader(Stream, bit_index, bitmap, State, Error)
|
|
% where
|
|
% [
|
|
% (get(_, Result, !Buffer) :-
|
|
% get_bit(Result, !Buffer)
|
|
% )
|
|
% ].
|
|
%
|
|
% :- instance stream.bulk_reader(read_stream(Stream, Error),
|
|
% bit_index, word, read_buffer(Stream, State, Error), Error)
|
|
% <= stream.bulk_reader(Stream, bit_index, bitmap, State, Error)
|
|
% where
|
|
% [
|
|
% (bulk_get(_, Index, NumBits, !Word, NumBitsRead, Result, !Buffer) :-
|
|
% get_bits(Index, NumBits, !Word, NumBitsRead, Result, !Buffer)
|
|
% )
|
|
% ].
|
|
%
|
|
% :- instance stream.bulk_reader(read_stream(Stream, Error),
|
|
% bit_index, bitmap, read_buffer(Stream, State, Error), Error)
|
|
% <= stream.bulk_reader(Stream, bit_index, bitmap, State, Error)
|
|
% where
|
|
% [
|
|
% (bulk_get(_, Index, NumBits, !BM, NumBitsRead, Result, !Buffer) :-
|
|
% get_bitmap(Index, NumBits, !BM, NumBitsRead, Result, !Buffer)
|
|
% )
|
|
% ].
|
|
|
|
:- end_module bit_buffer.read.
|