]> Repositorios git - scryer-prolog.git/commitdiff
copy_stream/2
authorJavier Sagredo <[email protected]>
Fri, 29 May 2026 00:16:35 +0000 (02:16 +0200)
committerJavier Sagredo <[email protected]>
Fri, 29 May 2026 00:16:35 +0000 (02:16 +0200)
build/instructions_template.rs
src/instructions.rs
src/lib/sockets.pl
src/machine/dispatch.rs
src/machine/system_calls.rs

index 7b8be1f826c1da57988707d3be3a67f56be3a1a5..1a0f6cce64494d8de4248f0560269adcc07df10c 100644 (file)
@@ -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")))]
index f029e46337b932a0041bc38fe8494952c93f9d80..7d9ace8f3020dc2e6e167d62637df270ab81358d 100644 (file)
@@ -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
index f66891617f3901968d986793ee8c5c236bbbdb89..4d2579d53c5904f71bbca2674d1e266529e0ce74 100644 (file)
@@ -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).
index 90a65cb19fbbbaa641915e82b266ce49bb93e3bf..267b9651475c867345d0585a36b57f6ce7f6f64e 100644 (file)
@@ -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);
index df15aeb56742db5d3299f2d1f2190baf0b89016f..f66d7c321ec4f5c1c48cba6e3350bcdaeefa70e8 100644 (file)
@@ -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);