From: Javier Sagredo Date: Fri, 29 May 2026 00:16:35 +0000 (+0200) Subject: copy_stream/2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=0c9d76c8ab95ab33390532601bbccc7b7b5af04c;p=scryer-prolog.git copy_stream/2 --- diff --git a/build/instructions_template.rs b/build/instructions_template.rs index 7b8be1f8..1a0f6cce 100644 --- a/build/instructions_template.rs +++ b/build/instructions_template.rs @@ -467,6 +467,8 @@ enum SystemClauseType { SocketServerAccept, #[strum_discriminants(strum(props(Arity = "1", Name = "$socket_server_close")))] SocketServerClose, + #[strum_discriminants(strum(props(Arity = "2", Name = "$copy_stream")))] + CopyStream, #[strum_discriminants(strum(props(Arity = "4", Name = "$tls_accept_client")))] TLSAcceptClient, #[strum_discriminants(strum(props(Arity = "3", Name = "$tls_client_connect")))] diff --git a/src/instructions.rs b/src/instructions.rs index f029e463..7d9ace8f 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -835,6 +835,7 @@ impl Instruction { | &Instruction::CallSocketServerOpen | &Instruction::CallSocketServerAccept | &Instruction::CallSocketServerClose + | &Instruction::CallCopyStream | &Instruction::CallTLSAcceptClient | &Instruction::CallTLSClientConnect | &Instruction::CallSucceed @@ -1094,6 +1095,7 @@ impl Instruction { | &Instruction::ExecuteSocketServerOpen | &Instruction::ExecuteSocketServerAccept | &Instruction::ExecuteSocketServerClose + | &Instruction::ExecuteCopyStream | &Instruction::ExecuteTLSAcceptClient | &Instruction::ExecuteTLSClientConnect | &Instruction::ExecuteSucceed diff --git a/src/lib/sockets.pl b/src/lib/sockets.pl index f6689161..4d2579d5 100644 --- a/src/lib/sockets.pl +++ b/src/lib/sockets.pl @@ -8,6 +8,7 @@ In both cases, with a stream, you can use the usual predicates to read and write socket_server_open/2, socket_server_accept/4, socket_server_close/1, + copy_stream/2, current_hostname/1]). :- use_module(library(error)). @@ -92,3 +93,11 @@ socket_server_close(ServerSocket) :- % Returns the current hostname of the computer in which Scryer Prolog is executing right now current_hostname(HostName) :- '$current_hostname'(HostName). + +%% copy_stream(+InputStream, +OutputStream). +% +% Copies all remaining bytes from InputStream to OutputStream in a single +% native call, without materialising the contents on the Prolog heap. +% Intended for streaming binary content (e.g. file -> TLS socket). +copy_stream(In, Out) :- + '$copy_stream'(In, Out). diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index 90a65cb1..267b9651 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -4938,6 +4938,14 @@ impl Machine { try_or_throw!(self.machine_st, self.socket_server_close(), continue); self.machine_st.p = self.machine_st.cp; } + &Instruction::CallCopyStream => { + try_or_throw!(self.machine_st, self.copy_stream(), continue); + step_or_fail!(self.machine_st, self.machine_st.p += 1); + } + &Instruction::ExecuteCopyStream => { + try_or_throw!(self.machine_st, self.copy_stream(), continue); + step_or_fail!(self.machine_st, self.machine_st.p = self.machine_st.cp); + } &Instruction::CallTLSAcceptClient => { #[cfg(feature = "tls")] try_or_throw!(self.machine_st, self.tls_accept_client(), continue); diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index df15aeb5..f66d7c32 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -7267,6 +7267,47 @@ impl Machine { Ok(()) } + #[inline(always)] + pub(crate) fn copy_stream(&mut self) -> CallResult { + let mut input = self.machine_st.get_stream_or_alias( + self.machine_st.registers[1], + &self.indices, + atom!("$copy_stream"), + 2, + )?; + let mut output = self.machine_st.get_stream_or_alias( + self.machine_st.registers[2], + &self.indices, + atom!("$copy_stream"), + 2, + )?; + + let stub_gen = || functor_stub(atom!("$copy_stream"), 2); + let mut buf = [0u8; 65536]; + + loop { + let n = match input.read(&mut buf) { + Ok(0) => break, + Ok(n) => n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(_) => { + let err = self + .machine_st + .existence_error(ExistenceError::Stream(input.into())); + return Err(self.machine_st.error_form(err, stub_gen())); + } + }; + if output.write_all(&buf[..n]).is_err() { + let err = self + .machine_st + .existence_error(ExistenceError::Stream(output.into())); + return Err(self.machine_st.error_form(err, stub_gen())); + } + } + + Ok(()) + } + #[inline(always)] pub(crate) fn socket_server_close(&mut self) -> CallResult { let culprit = self.deref_register(1);