mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +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.
442 lines
16 KiB
Mathematica
442 lines
16 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2007, 2011 The University of Melbourne
|
|
% Copyright (C) 2014-2016, 2018 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
% File: bit_buffer.write.m.
|
|
% Main author: stayl.
|
|
% Stability: low.
|
|
%
|
|
% A bit buffer provides an interface between bit-oriented output requests
|
|
% and byte-array-oriented streams, storing bits until there are enough bytes
|
|
% to make calling the `put' method on the stream worthwhile.
|
|
%
|
|
% 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.write.
|
|
:- interface.
|
|
|
|
:- import_module io.
|
|
|
|
:- type write_buffer(Stream, State).
|
|
% <= stream.writer(Stream, bitmap.slice, State).
|
|
|
|
:- type write_buffer == write_buffer(error_stream, error_state).
|
|
:- type io_write_buffer == write_buffer(io.binary_output_stream, io.state).
|
|
|
|
:- inst uniq_write_buffer == ground. % XXX Should be unique.
|
|
:- mode write_buffer_di == in(uniq_write_buffer).
|
|
:- mode write_buffer_ui == in(uniq_write_buffer).
|
|
:- mode write_buffer_uo == out(uniq_write_buffer).
|
|
|
|
% new(NumBytes, Stream, State) creates a buffer which will write to
|
|
% the stream specified by Stream and State in chunks of NumBytes bytes.
|
|
% If NumBytes is less than the size of an integer (given by
|
|
% int.bits_per_int), the size of an integer will be used instead.
|
|
%
|
|
:- func new(num_bytes, Stream, State) = write_buffer(Stream, State)
|
|
<= stream.writer(Stream, byte_index, State).
|
|
:- mode new(in, in, di) = write_buffer_uo is det.
|
|
|
|
% new(NumBytes):
|
|
%
|
|
% Create a buffer which collects all of the bits written, and does
|
|
% not write them to a stream. The bits are collected in chunks of
|
|
% size NumBytes bytes, and are written to a bitmap by
|
|
% `finalize_to_bitmap/1'.
|
|
%
|
|
:- func new_bitmap_builder(num_bytes) = write_buffer.
|
|
:- mode new_bitmap_builder(in) = out is det.
|
|
|
|
% How many bits to be written does the buffer contain?
|
|
%
|
|
:- func num_buffered_bits(write_buffer(_, _)) = num_bits.
|
|
:- mode num_buffered_bits(write_buffer_ui) = out is det.
|
|
|
|
% Return how many bits need to be written to get to a byte boundary
|
|
% in the output stream.
|
|
%
|
|
:- func num_bits_to_byte_boundary(write_buffer(_, _)) = num_bits.
|
|
:- mode num_bits_to_byte_boundary(write_buffer_ui) = out is det.
|
|
|
|
% Write a bit to the buffer.
|
|
%
|
|
:- pred put_bit(bool, write_buffer(Stream, State), write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode put_bit(in, write_buffer_di, write_buffer_uo) is det.
|
|
|
|
% Write the given number of low-order bits from an int to the buffer.
|
|
% The number of bits must be less than int.bits_per_int.
|
|
%
|
|
:- pred put_bits(word, num_bits, write_buffer(Stream, State),
|
|
write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode put_bits(in, in, write_buffer_di, write_buffer_uo) is det.
|
|
|
|
% Write the eight low-order bits from an int to the buffer.
|
|
% The number of bits must be less than int.bits_per_int.
|
|
%
|
|
:- pred put_byte(word, write_buffer(Stream, State),
|
|
write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode put_byte(in, write_buffer_di, write_buffer_uo) is det.
|
|
|
|
% Write bits from a bitmap to the buffer.
|
|
% The buffer does not keep a reference to the bitmap.
|
|
%
|
|
:- pred put_bitmap(bitmap, write_buffer(Stream, State),
|
|
write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode put_bitmap(bitmap_ui, write_buffer_di, write_buffer_uo) is det.
|
|
|
|
:- pred put_bitmap(bitmap, bit_index, num_bits,
|
|
write_buffer(Stream, State), write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode put_bitmap(bitmap_ui, in, in, write_buffer_di, write_buffer_uo) is det.
|
|
|
|
% Flush all complete bytes in the buffer to the output stream.
|
|
% If there is an incomplete final byte it will remain unwritten
|
|
% in the buffer.
|
|
%
|
|
:- pred flush(write_buffer(Stream, State), write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode flush(write_buffer_di, write_buffer_uo) is det.
|
|
|
|
% Pad the buffered data out to a byte boundary, flush it to
|
|
% the output stream, then return the Stream and State.
|
|
%
|
|
:- pred finalize(write_buffer(Stream, State), Stream, State)
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode finalize(write_buffer_di, out, uo) is det.
|
|
|
|
% Copy the data from a non-streamed write_buffer to a bitmap.
|
|
% The output is not padded to an even number of bits.
|
|
%
|
|
:- func finalize_to_bitmap(write_buffer) = bitmap.
|
|
:- mode finalize_to_bitmap(write_buffer_di) = bitmap_uo is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- implementation.
|
|
|
|
/*
|
|
:- interface.
|
|
% A write_buffer can be used as an output stream.
|
|
% XXX These instances don't work because of the duplicated variables
|
|
% in the head.
|
|
%
|
|
:- type write_stream(Stream) ---> write_stream.
|
|
:- instance stream.stream(write_stream(Stream), write_buffer(Stream, State))
|
|
<= stream.stream(Stream, State).
|
|
:- instance stream.output(write_stream(Stream), write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- instance stream.writer(write_stream(Stream), bool,
|
|
write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- instance stream.writer(write_stream(Stream), bitmap.slice,
|
|
write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
*/
|
|
|
|
% For a write_buffer, a bit_buffer is allocated that is bits_per_int
|
|
% larger than the size requested. This allows a full word to be
|
|
% written at any time. After each write, the position is checked.
|
|
% If the position is greater than the requested size, a chunk of the
|
|
% requested size is written to the stream. The unwritten bits are
|
|
% then copied to the start of the buffer.
|
|
%
|
|
% We always use a buffer size that is at least the size of a word
|
|
% so that writing a word to the buffer will require at most a single
|
|
% call to `put'. Allowing smaller sizes complicates the code
|
|
% for a case that shouldn't occur in practice.
|
|
%
|
|
% For a bitmap_builder, we store the filled bitmaps in a list rather
|
|
% than writing them to an output stream.
|
|
%
|
|
:- type write_buffer(Stream, State)
|
|
---> write_buffer(
|
|
bit_buffer :: bit_buffer(Stream, State)
|
|
).
|
|
|
|
new(NumBytes, Stream, State) = Buffer :-
|
|
SizeInBits = NumBytes * bits_per_byte,
|
|
Size = int.max(SizeInBits, bits_per_int),
|
|
BM = bitmap.init(Size + int.bits_per_int, no),
|
|
Buffer = write_buffer(new_buffer(BM, 0, Size, yes, Stream, State)).
|
|
|
|
new_bitmap_builder(NumBytes) = Buffer :-
|
|
Size = NumBytes * bits_per_byte,
|
|
BM = bitmap.init(Size + int.bits_per_int, no),
|
|
Buffer = write_buffer(new_buffer(BM, 0, Size, no,
|
|
error_stream, error_state)).
|
|
|
|
num_buffered_bits(write_buffer(Buffer)) =
|
|
Buffer ^ pos +
|
|
foldl((func(BM, N) = N + BM ^ num_bits), Buffer ^ filled_bitmaps, 0).
|
|
|
|
num_bits_to_byte_boundary(Buffer) = NumBits :-
|
|
Pos = Buffer ^ bit_buffer ^ pos,
|
|
Rem = Pos `unchecked_rem` bits_per_byte,
|
|
( if Rem = 0 then
|
|
NumBits = 0
|
|
else
|
|
NumBits = bits_per_byte - Rem
|
|
).
|
|
|
|
put_bit(yes, !Buffer) :-
|
|
put_bits(1, 1, !Buffer).
|
|
put_bit(no, !Buffer) :-
|
|
put_bits(0, 1, !Buffer).
|
|
|
|
put_bits(Bits, NumBits, write_buffer(!.Buffer), write_buffer(!:Buffer)) :-
|
|
BM0 = !.Buffer ^ bitmap,
|
|
Pos0 = !.Buffer ^ pos,
|
|
|
|
% We always make sure there is enough room in the buffer for a full
|
|
% word to be written, so this can't run off the end of the bitmap.
|
|
%
|
|
BM = BM0 ^ bits(Pos0, NumBits) := Bits,
|
|
Pos = Pos0 + NumBits,
|
|
set_bitmap(BM, Pos, !Buffer),
|
|
maybe_make_room(!Buffer).
|
|
|
|
put_byte(Byte, !Buffer) :-
|
|
put_bits(Byte, bits_per_byte, !Buffer).
|
|
|
|
put_bitmap(BM, !Buffer) :-
|
|
put_bitmap(BM, 0, BM ^ num_bits, !Buffer).
|
|
|
|
put_bitmap(BM, Index, NumBits,
|
|
write_buffer(!.Buffer), write_buffer(!:Buffer)) :-
|
|
put_bitmap_2(BM, Index, NumBits, !Buffer).
|
|
|
|
% XXX If we're writing to a list of bitmaps and the user doesn't want
|
|
% to write to the bitmap again, we should just add the bitmap passed
|
|
% by the user to the list of filled bitmaps, if the current buffer
|
|
% bitmap is full enough that we're not wasting too much space.
|
|
%
|
|
:- pred put_bitmap_2(bitmap, bit_index, num_bits,
|
|
bit_buffer(Stream, State), bit_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
:- mode put_bitmap_2(bitmap_ui, in, in,
|
|
write_buffer_di, write_buffer_uo) is det.
|
|
|
|
put_bitmap_2(BM, Index, NumBits, !Buffer) :-
|
|
( if NumBits = 0 then
|
|
true
|
|
else
|
|
BufferBM0 = !.Buffer ^ bitmap,
|
|
Pos = !.Buffer ^ pos,
|
|
Size = !.Buffer ^ size,
|
|
Remain = Size - Pos,
|
|
NumBitsToWrite = int.min(Remain, NumBits),
|
|
BufferBM = copy_bits(BM, Index, BufferBM0, Pos, NumBitsToWrite),
|
|
set_bitmap(BufferBM, Pos + NumBitsToWrite, !Buffer),
|
|
maybe_make_room(!Buffer),
|
|
put_bitmap_2(BM, Index + NumBitsToWrite,
|
|
NumBits - NumBitsToWrite, !Buffer)
|
|
).
|
|
|
|
flush(write_buffer(!.Buffer), write_buffer(!:Buffer)) :-
|
|
UseStream = !.Buffer ^ use_stream,
|
|
(
|
|
UseStream = yes,
|
|
flush_all_to_stream(!Buffer)
|
|
;
|
|
UseStream = no
|
|
).
|
|
|
|
:- pred flush_all_to_stream(bit_buffer(Stream, State)::bit_buffer_di,
|
|
bit_buffer(Stream, State)::bit_buffer_uo) is det
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
|
|
flush_all_to_stream(!Buffer) :-
|
|
( if num_buffered_bits(write_buffer(!.Buffer)) >= bits_per_byte then
|
|
flush_chunk_to_stream(!Buffer),
|
|
disable_warning [suspicious_recursion] (
|
|
flush_all_to_stream(!Buffer)
|
|
)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred flush_chunk_to_stream(bit_buffer(Stream, State)::bit_buffer_di,
|
|
bit_buffer(Stream, State)::bit_buffer_uo) is det
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
|
|
flush_chunk_to_stream(!Buffer) :-
|
|
% Write at most Size bytes at once (this is the output chunk size
|
|
% set in the call to `new').
|
|
Pos = !.Buffer ^ pos,
|
|
Size = !.Buffer ^ size,
|
|
NumBitsToWrite0 = int.min(Size, Pos),
|
|
NumBytes = NumBitsToWrite0 `unchecked_quotient` bits_per_byte,
|
|
( if NumBytes = 0 then
|
|
true
|
|
else
|
|
NumBitsToWrite = NumBytes * bits_per_byte,
|
|
stream.put(!.Buffer ^ stream,
|
|
bitmap.byte_slice(!.Buffer ^ bitmap, 0, NumBytes),
|
|
unsafe_promise_unique(!.Buffer ^ state), NewState),
|
|
Remain = Pos - NumBitsToWrite,
|
|
( if Remain = 0 then
|
|
NewBM = !.Buffer ^ bitmap
|
|
else
|
|
% Copy the remainder to the start of the bitmap.
|
|
% (We know that there are at most int.bits_per_int bits
|
|
% after the flush because that was the size of the bitmap
|
|
% created in `new', so we don't need to use copy_bits here).
|
|
NewBM0 = !.Buffer ^ bitmap,
|
|
NewBM = NewBM0 ^ bits(0, Remain) :=
|
|
NewBM0 ^ bits(NumBitsToWrite, Remain)
|
|
),
|
|
set_all(NewBM, Remain, !.Buffer ^ size, NewState,
|
|
!.Buffer ^ filled_bitmaps, !Buffer)
|
|
).
|
|
|
|
finalize(!.Buffer, Stream, State) :-
|
|
BitsToByte = num_bits_to_byte_boundary(!.Buffer),
|
|
put_bits(0, BitsToByte, !Buffer),
|
|
flush(!Buffer),
|
|
Stream = !.Buffer ^ bit_buffer ^ stream,
|
|
State = unsafe_promise_unique(!.Buffer ^ bit_buffer ^ state).
|
|
|
|
finalize_to_bitmap(write_buffer(Buffer)) = !:BM :-
|
|
NumBits = num_buffered_bits(write_buffer(Buffer)),
|
|
!:BM = bitmap.init(NumBits),
|
|
|
|
% Copy out the filled bitmaps starting at the end of the result bitmap.
|
|
%
|
|
LastBM = shrink_without_copying(Buffer ^ bitmap, Buffer ^ pos),
|
|
copy_out_bitmap(LastBM, NumBits, Index, !BM),
|
|
list.foldl2(copy_out_bitmap, Buffer ^ filled_bitmaps, Index, _, !BM).
|
|
|
|
% Copy the bitmap to the result bitmap, starting at the end.
|
|
%
|
|
:- pred copy_out_bitmap(bitmap::in, bit_index::in,
|
|
bit_index::out, bitmap::bitmap_di, bitmap::bitmap_uo) is det.
|
|
|
|
copy_out_bitmap(FilledBM, !Index, !BM) :-
|
|
Size = FilledBM ^ num_bits,
|
|
( if Size > 0 then
|
|
!:Index = !.Index - Size,
|
|
!:BM = bitmap.copy_bits(FilledBM, 0, !.BM, !.Index, Size)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred maybe_make_room(bit_buffer(Stream, State)::bit_buffer_di,
|
|
bit_buffer(Stream, State)::bit_buffer_uo) is det
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
|
|
maybe_make_room(!Buffer) :-
|
|
( if !.Buffer ^ pos >= !.Buffer ^ size then
|
|
make_room(!Buffer)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- pred make_room(bit_buffer(Stream, State)::bit_buffer_di,
|
|
bit_buffer(Stream, State)::bit_buffer_uo) is det
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
|
|
make_room(!Buffer) :-
|
|
UseStream = !.Buffer ^ use_stream,
|
|
(
|
|
UseStream = yes,
|
|
flush_chunk_to_stream(!Buffer)
|
|
;
|
|
UseStream = no,
|
|
store_full_buffer(!Buffer)
|
|
).
|
|
|
|
% This must only be called when the buffer has less than a word
|
|
% of space left.
|
|
%
|
|
:- pred store_full_buffer(bit_buffer(Stream, State)::in,
|
|
bit_buffer(Stream, State)::out) is det
|
|
<= stream.writer(Stream, bitmap.slice, State).
|
|
|
|
store_full_buffer(!Buffer) :-
|
|
Pos = !.Buffer ^ pos,
|
|
Size = !.Buffer ^ size,
|
|
OldBM = !.Buffer ^ bitmap,
|
|
State = !.Buffer ^ state,
|
|
|
|
% Double the buffer size at each allocation.
|
|
NewSize = (!.Buffer ^ size) * 2,
|
|
|
|
% Create the new bitmap, copying the left-over bytes from the old one.
|
|
% (We know that there are at most int.bits_per_int bits after the flush
|
|
% because that was the size of the bitmap created in `new', so
|
|
% we don't need to use copy_bits here).
|
|
NewBM0 = bitmap.init(NewSize + int.bits_per_int),
|
|
Remain = Pos - Size,
|
|
NewPos = Remain,
|
|
NewBM = NewBM0 ^ bits(0, Remain) := OldBM ^ bits(Size, Remain),
|
|
FilledBM = shrink_without_copying(OldBM, Size),
|
|
|
|
FilledBMs = [FilledBM | !.Buffer ^ filled_bitmaps],
|
|
|
|
set_all(NewBM, NewPos, NewSize,
|
|
unsafe_promise_unique(State), FilledBMs, !Buffer).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
/*
|
|
** XXX These instances don't work because of duplicated head variables.
|
|
|
|
:- instance stream.stream(write_stream(Stream), write_buffer(Stream, State))
|
|
<= stream.stream(Stream, State)
|
|
where
|
|
[
|
|
(name(_, Name, !Buffer) :-
|
|
BitBuffer0 = !.Buffer ^ bit_buffer,
|
|
State0 = BitBuffer0 ^ state,
|
|
Stream = BitBuffer0 ^ stream,
|
|
name(Stream, StreamName, State0, State),
|
|
Name = "bit_buffer.write.write_buffer(" ++ StreamName ++ ")",
|
|
set_state(State, BitBuffer0, BitBuffer),
|
|
!:Buffer = write_buffer(BitBuffer)
|
|
)
|
|
].
|
|
:- instance stream.output(write_stream(Stream), write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State)
|
|
where
|
|
[
|
|
(flush(_, !Buffer) :-
|
|
flush(!Buffer)
|
|
)
|
|
].
|
|
:- instance stream.writer(write_stream(Stream), bool,
|
|
write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State)
|
|
where
|
|
[
|
|
(put(_, Bit, !Buffer) :- put_bit(Bit, !Buffer))
|
|
].
|
|
:- instance stream.writer(write_stream(Stream), bitmap.slice,
|
|
write_buffer(Stream, State))
|
|
<= stream.writer(Stream, bitmap.slice, State)
|
|
where
|
|
[
|
|
(put(_, Slice, !Buffer) :-
|
|
put_bitmap(Slice ^ slice_bitmap, Slice ^ slice_start_bit_index,
|
|
Slice ^ slice_num_bits, !Buffer)
|
|
)
|
|
].
|
|
*/
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module bit_buffer.write.
|
|
%---------------------------------------------------------------------------%
|