mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 21:35:49 +00:00
440 lines
16 KiB
Mathematica
440 lines
16 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ts=4 sw=4 et tw=0 wm=0 ft=mercury
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2007, 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: 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)
|
|
).
|
|
|
|
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)
|
|
).
|
|
|
|
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),
|
|
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)
|
|
).
|
|
|
|
% 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.
|
|
%---------------------------------------------------------------------------%
|