Files
mercury/extras/net/streams.m
Mark Brown d465fa53cb Update the COPYING.LIB file and references to it.
Discussion of these changes can be found on the Mercury developers
mailing list archives from June 2018.

COPYING.LIB:
    Add a special linking exception to the LGPL.

*:
    Update references to COPYING.LIB.

    Clean up some minor errors that have accumulated in copyright
    messages.
2018-06-09 17:43:12 +10:00

229 lines
6.2 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2014-2016, 2018 The Mercury Team.
% This file is distributed under the terms specified in COPYING.LIB.
%-----------------------------------------------------------------------------%
%
% Module: net.streams.
% Main Author: Paul Bone
% Stability: low
%
% Provide a streams interface for sockets.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module net.streams.
:- interface.
:- import_module int.
:- import_module io.
:- import_module stream.
:- import_module string.
:- import_module net.sockets.
%-----------------------------------------------------------------------------%
:- type socket_stream.
:- func stream(socket) = socket_stream.
:- func socket(socket_stream) = socket.
:- type byte
---> byte(int).
:- type error.
:- instance error(streams.error).
:- instance stream(socket_stream, io).
%-----------------------------------------------------------------------------%
:- instance input(socket_stream, io).
% XXX: This does not buffer reads, it is slow.
%
:- instance reader(socket_stream, streams.byte, io, streams.error).
:- instance reader(socket_stream, line, io, streams.error).
%-----------------------------------------------------------------------------%
:- instance output(socket_stream, io).
% XXX: This does not buffer writes, it is slow.
%
:- instance writer(socket_stream, streams.byte, io).
:- instance writer(socket_stream, string, io).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module char.
:- import_module exception.
:- import_module bitmap.
:- import_module list.
:- import_module maybe.
:- import_module require.
:- type socket_stream
---> socket_stream(socket).
:- type error
---> error(string).
stream(Socket) = socket_stream(Socket).
socket(socket_stream(Socket)) = Socket.
%-----------------------------------------------------------------------------%
:- instance error(streams.error) where [
error_message(error(Str)) = Str
].
:- instance stream(socket_stream, io) where [
pred(name/4) is stream_name
].
:- pred stream_name(socket_stream::in, name::out, io::di, io::uo) is det.
stream_name(socket_stream(_Socket), Name, !IO) :-
Name = "a socket".
%-----------------------------------------------------------------------------%
:- instance input(socket_stream, io) where [].
:- instance reader(socket_stream, streams.byte, io, streams.error) where [
pred(get/4) is get_byte
].
:- pred get_byte(socket_stream::in, result(streams.byte, streams.error)::out,
io::di, io::uo) is det.
get_byte(socket_stream(Socket), Result, !IO) :-
read(Socket, 1, ReadResult, !IO),
(
ReadResult = ok(Bitmap),
( if num_bytes(Bitmap) = 1 then
Byte = Bitmap ^ byte(0),
Result = ok(byte(Byte))
else if num_bytes(Bitmap) = 0 then
Result = eof
else
unexpected($file, $pred,
"Read returned unexpected number of bytes")
)
;
ReadResult = eof,
Result = eof
;
ReadResult = error(String),
Result = error(error(String))
).
:- instance reader(socket_stream, line, io, streams.error) where [
pred(get/4) is get_line
].
:- pred get_line(socket_stream::in, result(line, streams.error)::out,
io::di, io::uo) is det.
get_line(Stream, Result, !IO) :-
get_chars_until_nl(Stream, [], Result0, !IO),
(
Result0 = ok(RevChars),
Result = ok(line(from_rev_char_list(RevChars)))
;
Result0 = eof,
Result = eof
;
Result0 = error(Error),
Result = error(Error)
).
:- pred get_chars_until_nl(Stream::in, list(char)::in,
result(list(char), Error)::out, State::di, State::uo) is det
<= reader(Stream, streams.byte, State, Error).
get_chars_until_nl(Stream, Chars0, Result, !IO) :-
get(Stream, ResByte, !IO),
(
ResByte = ok(byte(Byte)),
( char.from_int(Byte, Char) ->
( if
( Char = '\n'
; Char = '\r'
)
then
Result = ok(Chars0)
else
Chars1 = [Char | Chars0],
get_chars_until_nl(Stream, Chars1, Result, !IO)
)
;
unexpected($file, $pred, "Encoding error")
)
;
ResByte = eof,
Result = eof
;
ResByte = error(Error),
Result = error(Error)
).
%-----------------------------------------------------------------------------%
:- instance output(socket_stream, io) where [
pred(flush/3) is flush_noop
].
:- pred flush_noop(socket_stream::in, io::di, io::uo) is det.
flush_noop(_, !IO).
% XXX: This does not buffer writes, it is slow.
%
:- instance writer(socket_stream, streams.byte, io) where [
pred(put/4) is put_byte
].
:- pred put_byte(socket_stream::in, streams.byte::in, io::di, io::uo)
is det.
put_byte(socket_stream(Socket), byte(Byte), !IO) :-
Bitmap = init(bits_per_byte) ^ byte(0) := Byte,
write(Socket, Bitmap, Result, !IO),
(
Result = ok
;
Result = error(Error),
throw(streams.error(Error))
).
:- instance writer(socket_stream, string, io) where [
pred(put/4) is put_string
].
:- pred put_string(Stream::in, string::in, State::di, State::uo) is det
<= writer(Stream, streams.byte, State).
put_string(Stream, String, !State) :-
foldl(put_char(Stream), String, !State).
:- pred put_char(Stream::in, char::in, State::di, State::uo) is det
<= writer(Stream, streams.byte, State).
put_char(Stream, Char, !State) :-
put(Stream, byte(to_int(Char)), !State).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%