aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/jau/byte_stream.hpp165
-rw-r--r--java_base/org/jau/io/Buffers.java37
-rw-r--r--java_jni/jni/CMakeLists.txt1
-rw-r--r--java_jni/jni/jau/ByteInStream_Feed.cxx9
-rw-r--r--java_jni/jni/jau/ByteInStream_File.cxx15
-rw-r--r--java_jni/jni/jau/ByteInStream_URL.cxx10
-rw-r--r--java_jni/jni/jau/ByteOutStream_File.cxx260
-rw-r--r--java_jni/org/jau/io/ByteInStream.java38
-rw-r--r--java_jni/org/jau/io/ByteInStream_Feed.java5
-rw-r--r--java_jni/org/jau/io/ByteInStream_File.java3
-rw-r--r--java_jni/org/jau/io/ByteInStream_URL.java5
-rw-r--r--java_jni/org/jau/io/ByteOutStream.java105
-rw-r--r--java_jni/org/jau/io/ByteOutStream_File.java188
-rw-r--r--java_jni/org/jau/io/IOStateFunc.java59
-rw-r--r--src/byte_stream.cpp114
-rw-r--r--test/java/jau/test/fs/TestFileUtils01.java11
-rw-r--r--test/java/jau/test/io/TestByteStream01.java222
-rw-r--r--test/test_bytestream01.cpp61
-rw-r--r--test/test_fileutils01.cpp14
-rw-r--r--test/test_iostream01.cpp23
20 files changed, 1108 insertions, 237 deletions
diff --git a/include/jau/byte_stream.hpp b/include/jau/byte_stream.hpp
index e67d81c..11797c3 100644
--- a/include/jau/byte_stream.hpp
+++ b/include/jau/byte_stream.hpp
@@ -195,6 +195,9 @@ namespace jau::io {
ByteInStream& operator=(const ByteInStream&) = delete;
ByteInStream(const ByteInStream&) = delete;
+ /** Checks if the stream has an associated file. */
+ virtual bool is_open() const noexcept = 0;
+
/**
* Close the stream if supported by the underlying mechanism.
*/
@@ -357,6 +360,8 @@ namespace jau::io {
explicit ByteInStream_SecMemory(const std::vector<uint8_t>& in)
: m_source(in.begin(), in.end()), m_offset(0) {}
+ bool is_open() const noexcept override { return true; }
+
void close() noexcept override;
~ByteInStream_SecMemory() noexcept override { close(); }
@@ -399,9 +404,7 @@ namespace jau::io {
size_t peek(void*, size_t, size_t) noexcept override;
bool available(size_t n) noexcept override;
- /** Checks if the stream has an associated file. */
- constexpr bool is_open() const noexcept
- { return 0 <= m_fd; }
+ bool is_open() const noexcept override { return 0 <= m_fd; }
std::string id() const noexcept override { return stats.path(); }
@@ -461,7 +464,7 @@ namespace jau::io {
};
/**
- * This class represents a Ringbuffer-Based byte input stream with a URL connection provisioned data feed.
+ * Ringbuffer-Based byte input stream with a URL connection provisioned data feed.
*
* Standard implementation uses [curl](https://curl.se/),
* hence all [*libcurl* network protocols](https://curl.se/docs/url-syntax.html) are supported,
@@ -518,6 +521,8 @@ namespace jau::io {
ByteInStream_URL& operator=(const ByteInStream_URL&) = delete;
+ bool is_open() const noexcept override;
+
void close() noexcept override;
~ByteInStream_URL() noexcept override { close(); }
@@ -560,7 +565,7 @@ namespace jau::io {
std::unique_ptr<ByteInStream> to_ByteInStream(const std::string& path_or_uri, jau::fraction_i64 timeout=20_s) noexcept;
/**
- * This class represents a Ringbuffer-Based byte input stream with an externally provisioned data feed.
+ * Ringbuffer-Based byte input stream with an externally provisioned data feed.
*/
class ByteInStream_Feed final : public ByteInStream {
public:
@@ -613,6 +618,8 @@ namespace jau::io {
ByteInStream_Feed& operator=(const ByteInStream_Feed&) = delete;
+ bool is_open() const noexcept override;
+
void close() noexcept override;
~ByteInStream_Feed() noexcept override { close(); }
@@ -685,7 +692,7 @@ namespace jau::io {
};
/**
- * This class represents a wrapped byte input stream with the capability
+ * Wrapped byte input stream with the capability
* to record the read byte stream at will.
*
* Peek'ed bytes won't be recorded, only read bytes.
@@ -719,6 +726,8 @@ namespace jau::io {
ByteInStream_Recorder& operator=(const ByteInStream_Recorder&) = delete;
+ bool is_open() const noexcept override { return m_parent.is_open(); }
+
void close() noexcept override;
~ByteInStream_Recorder() noexcept override { close(); }
@@ -773,6 +782,150 @@ namespace jau::io {
bool m_is_recording;
};
+ /**
+ * Abstract byte output stream object,
+ * to write data to a sink.
+ *
+ * All method implementations are of `noexcept`.
+ *
+ * One may use fail() to detect whether an error has occurred.
+ */
+ class ByteOutStream : public iostate_func
+ {
+ public:
+ ByteOutStream() = default;
+ virtual ~ByteOutStream() noexcept = default;
+ ByteOutStream& operator=(const ByteOutStream&) = delete;
+ ByteOutStream(const ByteOutStream&) = delete;
+
+ /** Checks if the stream has an associated file. */
+ virtual bool is_open() const noexcept = 0;
+
+ /**
+ * Close the stream if supported by the underlying mechanism.
+ */
+ virtual void close() noexcept = 0;
+
+ /**
+ * Write to the data sink. Moves the internal offset so that every
+ * call to write will be appended to the sink.
+ *
+ * This method is not blocking beyond the transfer length bytes.
+ *
+ * @param in the input bytes to write out
+ * @param length the length of the byte array in
+ * @return length in bytes that were actually written
+ */
+ [[nodiscard]] virtual size_t write(const void* in, size_t length) noexcept = 0;
+
+ /**
+ * return the id of this data source
+ * @return std::string representing the id of this data source
+ */
+ virtual std::string id() const noexcept { return ""; }
+
+ /**
+ * Read one byte.
+ * @param out the byte to read to
+ * @return length in bytes that was actually read and put
+ * into out
+ */
+ size_t write_byte(uint8_t& out) noexcept;
+
+ /**
+ * Returns the output position indicator.
+ *
+ * @return number of bytes written so far.
+ */
+ virtual uint64_t tellp() const noexcept = 0;
+
+ virtual std::string to_string() const noexcept = 0;
+ };
+
+ /**
+ * File based byte output stream, including named file descriptor.
+ */
+ class ByteOutStream_File final : public ByteOutStream {
+ private:
+ jau::fs::file_stats stats;
+ /**
+ * We mimic std::ofstream via OS level file descriptor operations,
+ * giving us more flexibility and enabling use of openat() operations.
+ */
+ int m_fd;
+
+ // Remember: constexpr specifier used in a function or static data member (since C++17) declaration implies inline
+
+ public:
+ bool is_open() const noexcept override { return 0 <= m_fd; }
+
+ size_t write(const void*, size_t) noexcept override;
+
+ std::string id() const noexcept override { return stats.path(); }
+
+ /**
+ * Returns the file descriptor if is_open(), otherwise -1 for no file descriptor.
+ *
+ * @see is_open()
+ */
+ int fd() const noexcept { return m_fd; }
+
+ /**
+ * Construct a stream based byte output stream from filesystem path,
+ * either an existing or new file.
+ *
+ * In case the file already exists, the underlying file offset is positioned at the end of the file.
+ *
+ * In case the given path is a local file URI starting with `file://`, see jau::io::uri::is_local_file_protocol(),
+ * the leading `file://` is cut off and the remainder being used.
+ *
+ * @param path the path to the file, maybe a local file URI
+ * @param mode file protection mode for a new file, otherwise ignored.
+ */
+ ByteOutStream_File(const std::string& path, const jau::fs::fmode_t mode = jau::fs::fmode_t::def_file_prot) noexcept;
+
+ /**
+ * Construct a stream based byte output stream from filesystem path and parent directory file descriptor,
+ * either an existing or new file.
+ *
+ * In case the file already exists, the underlying file offset is positioned at the end of the file.
+ *
+ * In case the given path is a local file URI starting with `file://`, see jau::io::uri::is_local_file_protocol(),
+ * the leading `file://` is cut off and the remainder being used.
+ *
+ * @param dirfd parent directory file descriptor
+ * @param path the path to the file, maybe a local file URI
+ * @param mode file protection mode for a new file, otherwise ignored.
+ */
+ ByteOutStream_File(const int dirfd, const std::string& path, const jau::fs::fmode_t mode = jau::fs::fmode_t::def_file_prot) noexcept;
+
+ /**
+ * Construct a stream based byte output stream by duplicating given file descriptor
+ *
+ * In case the given path is a local file URI starting with `file://`, see jau::io::uri::is_local_file_protocol(),
+ * the leading `file://` is cut off and the remainder being used.
+ *
+ * @param fd file descriptor to duplicate leaving the given `fd` untouched
+ */
+ ByteOutStream_File(const int fd) noexcept;
+
+ ByteOutStream_File(const ByteOutStream_File&) = delete;
+
+ ByteOutStream_File& operator=(const ByteOutStream_File&) = delete;
+
+ void close() noexcept override;
+
+ ~ByteOutStream_File() noexcept override { close(); }
+
+ uint64_t tellp() const noexcept override { return m_bytes_consumed; }
+
+ std::string to_string() const noexcept override;
+
+ private:
+ uint64_t m_bytes_consumed;
+ };
+
+
/**@}*/
} // namespace elevator::io
diff --git a/java_base/org/jau/io/Buffers.java b/java_base/org/jau/io/Buffers.java
index 524a715..a930958 100644
--- a/java_base/org/jau/io/Buffers.java
+++ b/java_base/org/jau/io/Buffers.java
@@ -87,8 +87,8 @@ public class Buffers {
/**
* Helper routine to get the Buffer byte offset by taking into
- * account the Buffer position and the underlying type. This is
- * the total offset for Direct Buffers.
+ * account the Buffer position and the underlying type.
+ * This is the total offset for Direct Buffers.
*
* Return value is of type `long` only to cover the `int` multiple of the position and element type size.<br/>
* For ByteBuffer, the return value can be safely cast to `int`.
@@ -119,6 +119,39 @@ public class Buffers {
}
/**
+ * Helper routine to get the Buffer byte limit by taking into
+ * account the Buffer limit and the underlying type.
+ * This is the total limit for Direct Buffers.
+ *
+ * Return value is of type `long` only to cover the `int` multiple of the position and element type size.<br/>
+ * For ByteBuffer, the return value can be safely cast to `int`.
+ */
+ public static long getDirectBufferByteLimit(final Object buf) {
+ if (buf == null) {
+ return 0;
+ }
+ if (buf instanceof Buffer) {
+ final long limit = ((Buffer) buf).limit();
+ if (buf instanceof ByteBuffer) {
+ return limit;
+ } else if (buf instanceof FloatBuffer) {
+ return limit * SIZEOF_FLOAT;
+ } else if (buf instanceof IntBuffer) {
+ return limit * SIZEOF_INT;
+ } else if (buf instanceof ShortBuffer) {
+ return limit * SIZEOF_SHORT;
+ } else if (buf instanceof DoubleBuffer) {
+ return limit * SIZEOF_DOUBLE;
+ } else if (buf instanceof LongBuffer) {
+ return limit * SIZEOF_LONG;
+ } else if (buf instanceof CharBuffer) {
+ return limit * SIZEOF_CHAR;
+ }
+ }
+ throw new IllegalArgumentException("Disallowed array backing store type in buffer " + buf.getClass().getName());
+ }
+
+ /**
* Access to NIO {@link sun.misc.Cleaner}, allowing caller to deterministically clean a given {@link sun.nio.ch.DirectBuffer}.
*/
public static class Cleaner {
diff --git a/java_jni/jni/CMakeLists.txt b/java_jni/jni/CMakeLists.txt
index 97bd124..4b5fe45 100644
--- a/java_jni/jni/CMakeLists.txt
+++ b/java_jni/jni/CMakeLists.txt
@@ -31,6 +31,7 @@ set (jaulib_jni_JNI_SRCS
${PROJECT_SOURCE_DIR}/java_jni/jni/jau/ByteInStream_File.cxx
${PROJECT_SOURCE_DIR}/java_jni/jni/jau/ByteInStream_URL.cxx
${PROJECT_SOURCE_DIR}/java_jni/jni/jau/ByteInStream_Feed.cxx
+ ${PROJECT_SOURCE_DIR}/java_jni/jni/jau/ByteOutStream_File.cxx
)
if(WIN32)
diff --git a/java_jni/jni/jau/ByteInStream_Feed.cxx b/java_jni/jni/jau/ByteInStream_Feed.cxx
index e7c7e77..76bd517 100644
--- a/java_jni/jni/jau/ByteInStream_Feed.cxx
+++ b/java_jni/jni/jau/ByteInStream_Feed.cxx
@@ -66,6 +66,15 @@ void Java_org_jau_io_ByteInStream_1Feed_dtorImpl(JNIEnv *env, jclass clazz, jlon
}
}
+jboolean Java_org_jau_io_ByteInStream_1Feed_is_1open(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteInStream_Feed> ref(env, obj); // hold until done
+ return ref->is_open() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_FALSE;
+}
void Java_org_jau_io_ByteInStream_1Feed_clearImpl(JNIEnv *env, jobject obj, jint mask) {
try {
diff --git a/java_jni/jni/jau/ByteInStream_File.cxx b/java_jni/jni/jau/ByteInStream_File.cxx
index 6e6fb4b..eb4421f 100644
--- a/java_jni/jni/jau/ByteInStream_File.cxx
+++ b/java_jni/jni/jau/ByteInStream_File.cxx
@@ -31,11 +31,6 @@
// #include "org_jau_io_IOState.h"
#include "org_jau_io_ByteInStream_File.h"
-
-extern "C" {
- #include <fcntl.h>
-}
-
//
// IOState
//
@@ -115,6 +110,16 @@ void Java_org_jau_io_ByteInStream_1File_dtorImpl(JNIEnv *env, jclass clazz, jlon
}
}
+jboolean Java_org_jau_io_ByteInStream_1File_is_1open(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteInStream_File> ref(env, obj); // hold until done
+ return ref->is_open() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_FALSE;
+}
+
void Java_org_jau_io_ByteInStream_1File_clearImpl(JNIEnv *env, jobject obj, jint mask) {
try {
jau::jni::shared_ptr_ref<jau::io::ByteInStream_File> ref(env, obj); // hold until done
diff --git a/java_jni/jni/jau/ByteInStream_URL.cxx b/java_jni/jni/jau/ByteInStream_URL.cxx
index 17892d5..7a634ac 100644
--- a/java_jni/jni/jau/ByteInStream_URL.cxx
+++ b/java_jni/jni/jau/ByteInStream_URL.cxx
@@ -66,6 +66,16 @@ void Java_org_jau_io_ByteInStream_1URL_dtorImpl(JNIEnv *env, jclass clazz, jlong
}
}
+jboolean Java_org_jau_io_ByteInStream_1URL_is_1open(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteInStream_URL> ref(env, obj); // hold until done
+ return ref->is_open() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_FALSE;
+}
+
void Java_org_jau_io_ByteInStream_1URL_clearImpl(JNIEnv *env, jobject obj, jint mask) {
try {
jau::jni::shared_ptr_ref<jau::io::ByteInStream_URL> ref(env, obj); // hold until done
diff --git a/java_jni/jni/jau/ByteOutStream_File.cxx b/java_jni/jni/jau/ByteOutStream_File.cxx
new file mode 100644
index 0000000..8966e2a
--- /dev/null
+++ b/java_jni/jni/jau/ByteOutStream_File.cxx
@@ -0,0 +1,260 @@
+/*
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2022 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "jau/byte_stream.hpp"
+
+#include <jau/debug.hpp>
+
+#include "jau/jni/helper_jni.hpp"
+
+#include "org_jau_io_ByteOutStream_File.h"
+
+//
+// ByteOutStream_File
+//
+
+jlong Java_org_jau_io_ByteOutStream_1File_ctorImpl1(JNIEnv *env, jclass cls, jstring jpath, jint jmode) {
+ try {
+ (void)cls;
+ // new instance
+ const std::string _path = jau::jni::from_jstring_to_string(env, jpath);
+ const jau::fs::fmode_t mode = static_cast<jau::fs::fmode_t>(jmode);
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref( new jau::io::ByteOutStream_File( _path, mode ) );
+ return ref.release_to_jlong();
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return (jlong) (intptr_t)nullptr;
+}
+
+jlong Java_org_jau_io_ByteOutStream_1File_ctorImpl2(JNIEnv *env, jclass cls, jint dirfd, jstring jpath, jint jmode) {
+ try {
+ (void)cls;
+ // new instance
+ const std::string path = jau::jni::from_jstring_to_string(env, jpath);
+ const jau::fs::fmode_t mode = static_cast<jau::fs::fmode_t>(jmode);
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref( new jau::io::ByteOutStream_File(dirfd, path, mode) );
+ return ref.release_to_jlong();
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return (jlong) (intptr_t)nullptr;
+}
+
+jlong Java_org_jau_io_ByteOutStream_1File_ctorImpl3(JNIEnv *env, jclass cls, jint fd) {
+ try {
+ (void)cls;
+ // new instance
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref( new jau::io::ByteOutStream_File(fd) );
+ return ref.release_to_jlong();
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return (jlong) (intptr_t)nullptr;
+}
+
+void Java_org_jau_io_ByteOutStream_1File_closeStream(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ ref->close();
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+}
+
+void Java_org_jau_io_ByteOutStream_1File_dtorImpl(JNIEnv *env, jclass clazz, jlong nativeInstance) {
+ (void)clazz;
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> sref(nativeInstance, false /* throw_on_nullptr */); // hold copy until done
+ if( nullptr != sref.pointer() ) {
+ std::shared_ptr<jau::io::ByteOutStream_File>* sref_ptr = jau::jni::castInstance<jau::io::ByteOutStream_File>(nativeInstance);
+ delete sref_ptr;
+ }
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+}
+
+jboolean Java_org_jau_io_ByteOutStream_1File_is_1open(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return ref->is_open() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_FALSE;
+}
+
+void Java_org_jau_io_ByteOutStream_1File_clearImpl(JNIEnv *env, jobject obj, jint mask) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ ref->clear( static_cast<jau::io::iostate>(mask) );
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+}
+
+jint Java_org_jau_io_ByteOutStream_1File_fd(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return ref->fd();
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return -1;
+}
+
+jint Java_org_jau_io_ByteOutStream_1File_rdStateImpl(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return static_cast<jint>( ref->rdstate() );
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return static_cast<jint>(jau::io::iostate::failbit);
+}
+
+void Java_org_jau_io_ByteOutStream_1File_setStateImpl(JNIEnv *env, jobject obj, jint mask) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ ref->setstate( static_cast<jau::io::iostate>(mask) );
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+}
+
+jboolean Java_org_jau_io_ByteOutStream_1File_good(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return ref->good() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_org_jau_io_ByteOutStream_1File_eof(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return ref->eof() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_TRUE;
+}
+
+jboolean Java_org_jau_io_ByteOutStream_1File_fail(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return ref->fail() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_TRUE;
+}
+
+jboolean Java_org_jau_io_ByteOutStream_1File_bad(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return ref->bad() ? JNI_TRUE : JNI_FALSE;
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return JNI_FALSE;
+}
+
+jint Java_org_jau_io_ByteOutStream_1File_write(JNIEnv *env, jobject obj, jbyteArray jin, jint joffset, jint jlength) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+
+ if( nullptr == jin ) {
+ throw jau::IllegalArgumentException("in buffer null", E_FILE_LINE);
+ }
+ const size_t in_size = env->GetArrayLength(jin);
+ if( (size_t)joffset + (size_t)jlength > in_size ) {
+ throw jau::IllegalArgumentException("input byte size "+std::to_string(in_size)+" < "+std::to_string(joffset)+" + "+std::to_string(jlength), E_FILE_LINE);
+ }
+ jau::jni::JNICriticalArray<uint8_t, jbyteArray> criticalArray(env); // RAII - release
+ uint8_t * in_ptr = criticalArray.get(jin, criticalArray.Mode::NO_UPDATE_AND_RELEASE);
+ if( NULL == in_ptr ) {
+ throw jau::InternalError("GetPrimitiveArrayCritical(address byte array) is null", E_FILE_LINE);
+ }
+ return (jint) ref->write(in_ptr + joffset, jlength);
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return 0;
+}
+
+jint Java_org_jau_io_ByteOutStream_1File_write2Impl(JNIEnv *env, jobject obj, jobject jin, jint out_offset, jint in_limit) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+
+ if( nullptr == jin ) {
+ throw jau::IllegalArgumentException("in buffer null", E_FILE_LINE);
+ }
+ if( 0 > in_limit) {
+ throw jau::IllegalArgumentException("invalid negative limit", E_FILE_LINE);
+ }
+ uint8_t * in_ptr = static_cast<uint8_t *>( env->GetDirectBufferAddress(jin) );
+ if( nullptr == in_ptr ) {
+ throw jau::IllegalArgumentException("in buffer access failure", E_FILE_LINE);
+ }
+ return (jint) ref->write(in_ptr + out_offset, in_limit - out_offset);
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return 0;
+}
+
+jstring Java_org_jau_io_ByteOutStream_1File_id(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return jau::jni::from_string_to_jstring(env, ref->id());
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return nullptr;
+}
+
+jlong Java_org_jau_io_ByteOutStream_1File_tellp(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj); // hold until done
+ return static_cast<jlong>( ref->tellp() );
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return 0;
+}
+
+jstring Java_org_jau_io_ByteOutStream_1File_toString(JNIEnv *env, jobject obj) {
+ try {
+ jau::jni::shared_ptr_ref<jau::io::ByteOutStream_File> ref(env, obj, false /* throw_on_nullptr */); // hold until done
+ std::string str = ref.is_null() ? "null" : ref->to_string();
+ return jau::jni::from_string_to_jstring(env, str);
+ } catch(...) {
+ rethrow_and_raise_java_exception_jau(env);
+ }
+ return nullptr;
+}
diff --git a/java_jni/org/jau/io/ByteInStream.java b/java_jni/org/jau/io/ByteInStream.java
index 407f08b..9303635 100644
--- a/java_jni/org/jau/io/ByteInStream.java
+++ b/java_jni/org/jau/io/ByteInStream.java
@@ -50,7 +50,10 @@ import java.nio.ByteBuffer;
*
* @see @ref byte_in_stream_properties "ByteInStream Properties"
*/
-public interface ByteInStream extends AutoCloseable {
+public interface ByteInStream extends IOStateFunc, AutoCloseable {
+ /** Checks if the stream has an associated file. */
+ boolean is_open();
+
/**
* Close the stream if supported by the underlying mechanism.
*
@@ -71,35 +74,6 @@ public interface ByteInStream extends AutoCloseable {
@Override
void close();
- /** Clears state flags by assignment to the given value. */
- void clear(final IOState state);
-
- /**
- * Returns the current state flags.
- *
- * Method is marked `virtual` to allow implementations with asynchronous resources
- * to determine or update the current iostate.
- *
- * Method is used throughout all query members and setstate(),
- * hence they all will use the updated state from a potential override implementation.
- */
- IOState rdState();
-
- /** Sets state flags, by keeping its previous bits. */
- void setState(final IOState state);
-
- /** Checks if no error nor eof() has occurred i.e. I/O operations are available. */
- boolean good();
-
- /** Checks if end-of-file has been reached. */
- boolean eof();
-
- /** Checks if an error has occurred. */
- boolean fail();
-
- /** Checks if a non-recoverable error has occurred. */
- boolean bad();
-
/**
* Test whether the source still has data that can be read, synonym for !good().
*
@@ -147,7 +121,7 @@ public interface ByteInStream extends AutoCloseable {
* See details of the implementing class.
*
* @param out the byte array to write the result to
- * @param offset offset to in byte array to read into
+ * @param offset offset to byte array to read into
* @param length the length of the byte array out
* @return length in bytes that was actually read and put into out
*
@@ -182,7 +156,7 @@ public interface ByteInStream extends AutoCloseable {
* the source starting at the same position.
*
* @param out the byte array to write the output to
- * @param offset offset to in byte array to read into
+ * @param offset offset to byte array to read into
* @param length number of in bytes to read into starting at offset
* @param peek_offset the offset into the stream to read at
* @return length in bytes that was actually read and put into out
diff --git a/java_jni/org/jau/io/ByteInStream_Feed.java b/java_jni/org/jau/io/ByteInStream_Feed.java
index 96faa3f..ceb9127 100644
--- a/java_jni/org/jau/io/ByteInStream_Feed.java
+++ b/java_jni/org/jau/io/ByteInStream_Feed.java
@@ -26,7 +26,7 @@ package org.jau.io;
import java.nio.ByteBuffer;
/**
- * This class represents a Ringbuffer-Based byte input stream with an externally provisioned data feed.
+ * Ringbuffer-Based byte input stream with an externally provisioned data feed.
*
* Instance uses the native C++ object `jau::io::ByteInStream_Feed`.
*/
@@ -71,6 +71,9 @@ public final class ByteInStream_Feed implements ByteInStream {
}
@Override
+ public native boolean is_open();
+
+ @Override
public void clear(final IOState state) {
clearImpl( state.mask );
}
diff --git a/java_jni/org/jau/io/ByteInStream_File.java b/java_jni/org/jau/io/ByteInStream_File.java
index ff2448f..de75de6 100644
--- a/java_jni/org/jau/io/ByteInStream_File.java
+++ b/java_jni/org/jau/io/ByteInStream_File.java
@@ -117,6 +117,9 @@ public final class ByteInStream_File implements ByteInStream {
}
@Override
+ public native boolean is_open();
+
+ @Override
public void clear(final IOState state) {
clearImpl( state.mask );
}
diff --git a/java_jni/org/jau/io/ByteInStream_URL.java b/java_jni/org/jau/io/ByteInStream_URL.java
index e4d0a68..a9e79ad 100644
--- a/java_jni/org/jau/io/ByteInStream_URL.java
+++ b/java_jni/org/jau/io/ByteInStream_URL.java
@@ -26,7 +26,7 @@ package org.jau.io;
import java.nio.ByteBuffer;
/**
- * This class represents a Ringbuffer-Based byte input stream with a URL connection provisioned data feed.
+ * Ringbuffer-Based byte input stream with a URL connection provisioned data feed.
*
* Instance uses the native C++ object `jau::io::ByteInStream_URL`.
*
@@ -75,6 +75,9 @@ public final class ByteInStream_URL implements ByteInStream {
}
@Override
+ public native boolean is_open();
+
+ @Override
public void clear(final IOState state) {
clearImpl( state.mask );
}
diff --git a/java_jni/org/jau/io/ByteOutStream.java b/java_jni/org/jau/io/ByteOutStream.java
new file mode 100644
index 0000000..9862906
--- /dev/null
+++ b/java_jni/org/jau/io/ByteOutStream.java
@@ -0,0 +1,105 @@
+/**
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2022 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package org.jau.io;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Abstract byte output stream object,
+ * to write data to a sink.
+ *
+ * Its specializations utilize a native C++ implementation
+ * derived from `jau::io::ByteOutStream`.
+ *
+ * All method implementations are of `noexcept`.
+ *
+ * One may use fail() to detect whether an error has occurred.
+ */
+public interface ByteOutStream extends IOStateFunc, AutoCloseable {
+ /** Checks if the stream has an associated file. */
+ boolean is_open();
+
+ /**
+ * Close the stream if supported by the underlying mechanism.
+ *
+ * Native instance will not be disposed.
+ *
+ * {@inheritDoc}
+ */
+ void closeStream();
+
+ /**
+ * Close the stream if supported by the underlying mechanism
+ * and dispose the native instance.
+ *
+ * Instance is unusable after having this method called.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ void close();
+
+ /**
+ * Write to the data sink. Moves the internal offset so that every
+ * call to write will be appended to the sink.
+ *
+ * This method is not blocking beyond the transfer length bytes.
+ *
+ * @param in the input bytes to write out
+ * @param offset offset to byte array to write out
+ * @param length the length of the byte array in
+ * @return length in bytes that were actually written
+ */
+ int write(byte in[], final int offset, final int length);
+
+ /**
+ * Write to the data sink. Moves the internal offset so that every
+ * call to write will be appended to the sink.
+ *
+ * This method is not blocking beyond the transfer length bytes.
+ *
+ * @param in the direct {@link ByteBuffer} source to be written to the sink from {@link ByteBuffer#position() position} up to its {@link ByteBuffer#limit() limit},
+ * i.e. {@link ByteBuffer#remaining() remaining}.
+ * {@link ByteBuffer#position() Position} will be set to {@link ByteBuffer#position() position} + written-bytes.
+ * @return length in bytes that were actually written,
+ * equal to its current {@link ByteBuffer#position() position} - previous {@link ByteBuffer#position() position}.
+ */
+ int write(ByteBuffer in);
+
+ /**
+ * return the id of this data source
+ * @return std::string representing the id of this data source
+ */
+ String id();
+
+ /**
+ * Returns the output position indicator.
+ *
+ * @return number of bytes written so far.
+ */
+ long tellp();
+
+ @Override
+ String toString();
+}
diff --git a/java_jni/org/jau/io/ByteOutStream_File.java b/java_jni/org/jau/io/ByteOutStream_File.java
new file mode 100644
index 0000000..53ca9d7
--- /dev/null
+++ b/java_jni/org/jau/io/ByteOutStream_File.java
@@ -0,0 +1,188 @@
+/**
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2022 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package org.jau.io;
+
+import java.nio.ByteBuffer;
+
+import org.jau.fs.FMode;
+
+/**
+ * File based byte output stream, including named file descriptor.
+ *
+ * Implementation mimics std::ifstream via OS level file descriptor (FD) operations,
+ * giving more flexibility, allowing reusing existing FD and enabling openat() operations.
+ *
+ * Instance uses the native C++ object `jau::io::ByteOutStream_File`.
+ */
+public final class ByteOutStream_File implements ByteOutStream {
+ private volatile long nativeInstance;
+ /* pp */ long getNativeInstance() { return nativeInstance; }
+
+ /**
+ * Construct a stream based byte output stream from filesystem path,
+ * either an existing or new file.
+ *
+ * In case the file already exists, the underlying file offset is positioned at the end of the file.
+ *
+ * In case the given path is a local file URI starting with `file://`, see jau::io::uri::is_local_file_protocol(),
+ * the leading `file://` is cut off and the remainder being used.
+ *
+ * @param path the path to the file, maybe a local file URI
+ * @param mode file protection mode for a new file, otherwise ignored, may use {@link FMode#def_file}.
+ */
+ public ByteOutStream_File(final String path, final FMode mode) {
+ try {
+ nativeInstance = ctorImpl1(path, mode.mask);
+ } catch (final Throwable t) {
+ System.err.println("ByteOutStream_File.ctor: native ctor failed: "+t.getMessage());
+ throw t;
+ }
+ }
+ private static native long ctorImpl1(final String path, int mode);
+
+ /**
+ * Construct a stream based byte output stream from filesystem path and parent directory file descriptor,
+ * either an existing or new file.
+ *
+ * In case the file already exists, the underlying file offset is positioned at the end of the file.
+ *
+ * In case the given path is a local file URI starting with `file://`, see jau::io::uri::is_local_file_protocol(),
+ * the leading `file://` is cut off and the remainder being used.
+ *
+ * @param dirfd parent directory file descriptor
+ * @param path the path to the file, maybe a local file URI
+ * @param mode file protection mode for a new file, otherwise ignored, may use {@link FMode#def_file}.
+ */
+ public ByteOutStream_File(final int dirfd, final String path, final FMode mode) {
+ try {
+ nativeInstance = ctorImpl2(dirfd, path, mode.mask);
+ } catch (final Throwable t) {
+ System.err.println("ByteOutStream_File.ctor: native ctor failed: "+t.getMessage());
+ throw t;
+ }
+ }
+ private static native long ctorImpl2(final int dirfd, final String path, int mode);
+
+ /**
+ * Construct a stream based byte output stream by duplicating given file descriptor
+ *
+ * In case the given path is a local file URI starting with `file://`, see jau::io::uri::is_local_file_protocol(),
+ * the leading `file://` is cut off and the remainder being used.
+ *
+ * @param fd file descriptor to duplicate leaving the given `fd` untouched
+ */
+ public ByteOutStream_File(final int fd) {
+ try {
+ nativeInstance = ctorImpl3(fd);
+ } catch (final Throwable t) {
+ System.err.println("ByteOutStream_File.ctor: native ctor failed: "+t.getMessage());
+ throw t;
+ }
+ }
+ private static native long ctorImpl3(final int fd);
+
+ @Override
+ public native void closeStream();
+
+ @Override
+ public void close() {
+ final long handle;
+ synchronized( this ) {
+ handle = nativeInstance;
+ nativeInstance = 0;
+ }
+ if( 0 != handle ) {
+ dtorImpl(handle);
+ }
+ }
+ private static native void dtorImpl(final long nativeInstance);
+
+ @Override
+ public void finalize() {
+ close();
+ }
+
+ @Override
+ public native boolean is_open();
+
+ @Override
+ public void clear(final IOState state) {
+ clearImpl( state.mask );
+ }
+ private native void clearImpl(int s);
+
+ /**
+ * Returns the file descriptor if is_open(), otherwise -1 for no file descriptor.
+ *
+ * @see is_open()
+ */
+ public native int fd();
+
+ @Override
+ public IOState rdState() {
+ return new IOState( rdStateImpl() );
+ }
+ private native int rdStateImpl();
+
+ @Override
+ public void setState(final IOState state) {
+ setStateImpl( state.mask );
+ }
+ private native void setStateImpl(int s);
+
+ @Override
+ public native boolean good();
+
+ @Override
+ public native boolean eof();
+
+ @Override
+ public native boolean fail();
+
+ @Override
+ public native boolean bad();
+
+ @Override
+ public native int write(final byte[] in, final int offset, final int length);
+
+ @Override
+ public int write(final ByteBuffer in) {
+ if( !Buffers.isDirect(in) ) {
+ throw new IllegalArgumentException("in buffer not direct");
+ }
+ final int res = write2Impl(in, (int)Buffers.getDirectBufferByteOffset(in), (int)Buffers.getDirectBufferByteLimit(in));
+ in.position(in.position() + res);
+ return res;
+ }
+ private native int write2Impl(Object in, int in_offset, int in_limit);
+
+ @Override
+ public native String id();
+
+ @Override
+ public native long tellp();
+
+ @Override
+ public native String toString();
+}
diff --git a/java_jni/org/jau/io/IOStateFunc.java b/java_jni/org/jau/io/IOStateFunc.java
new file mode 100644
index 0000000..02fa759
--- /dev/null
+++ b/java_jni/org/jau/io/IOStateFunc.java
@@ -0,0 +1,59 @@
+/**
+ * Author: Sven Gothel <[email protected]>
+ * Copyright (c) 2022 Gothel Software e.K.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package org.jau.io;
+
+/**
+ * Supporting std::basic_ios's iostate functionality for all ByteInStream implementations.
+ */
+public interface IOStateFunc {
+
+ /** Clears state flags by assignment to the given value. */
+ void clear(final IOState state);
+
+ /**
+ * Returns the current state flags.
+ *
+ * Method is marked `virtual` to allow implementations with asynchronous resources
+ * to determine or update the current iostate.
+ *
+ * Method is used throughout all query members and setstate(),
+ * hence they all will use the updated state from a potential override implementation.
+ */
+ IOState rdState();
+
+ /** Sets state flags, by keeping its previous bits. */
+ void setState(final IOState state);
+
+ /** Checks if no error nor eof() has occurred i.e. I/O operations are available. */
+ boolean good();
+
+ /** Checks if end-of-file has been reached. */
+ boolean eof();
+
+ /** Checks if an error has occurred. */
+ boolean fail();
+
+ /** Checks if a non-recoverable error has occurred. */
+ boolean bad();
+}
diff --git a/src/byte_stream.cpp b/src/byte_stream.cpp
index b834003..ac90ab8 100644
--- a/src/byte_stream.cpp
+++ b/src/byte_stream.cpp
@@ -375,6 +375,11 @@ bool ByteInStream_URL::available(size_t n) noexcept {
return m_buffer.waitForElements(n, m_timeout) >= n;
}
+bool ByteInStream_URL::is_open() const noexcept {
+ // url thread ended, only remaining bytes in buffer available left
+ return async_io_result_t::NONE != m_result && m_buffer.size() > 0;
+}
+
size_t ByteInStream_URL::read(void* out, size_t length) noexcept {
if( 0 == length || end_of_data() ) {
return 0;
@@ -464,6 +469,11 @@ bool ByteInStream_Feed::available(size_t n) noexcept {
return m_buffer.waitForElements(n, m_timeout) >= n;
}
+bool ByteInStream_Feed::is_open() const noexcept {
+ // url thread ended, only remaining bytes in buffer available left
+ return async_io_result_t::NONE != m_result && m_buffer.size() > 0;
+}
+
size_t ByteInStream_Feed::read(void* out, size_t length) noexcept {
if( 0 == length || end_of_data() ) {
return 0;
@@ -558,3 +568,107 @@ std::string ByteInStream_Recorder::to_string() const noexcept {
"], consumed "+jau::to_decstring(m_bytes_consumed)+
", iostate["+jau::io::to_string(rdstate())+"]]";
}
+
+size_t ByteOutStream_File::write(const void* out, size_t length) noexcept {
+ if( 0 == length || fail() ) {
+ return 0;
+ }
+ const uint8_t* out_u8 = static_cast<const uint8_t*>(out);
+ size_t total = 0;
+ while( total < length ) {
+ ssize_t len;
+ while ( ( len = ::write(m_fd, out_u8+total, length-total) ) < 0 ) {
+ if ( errno == EAGAIN || errno == EINTR ) {
+ // cont temp unavail or interruption
+ // unlikely for regular files and we open w/o O_NONBLOCK
+ // - EAGAIN (file) and EWOULDBLOCK (socket) if blocking
+ // - EINTR (signal)
+ continue;
+ }
+ // Check errno == ETIMEDOUT ??
+ setstate_impl( iostate::failbit );
+ DBG_PRINT("ByteOutStream_File::write: Error occurred in %s, errno %d %s", to_string().c_str(), errno, strerror(errno));
+ return 0;
+ }
+ total += static_cast<size_t>(len);
+ if( 0 == len ) {
+ setstate_impl( iostate::failbit);
+ break;
+ }
+ }
+ m_bytes_consumed += total;
+ return total;
+}
+
+ByteOutStream_File::ByteOutStream_File(const int fd) noexcept
+: stats(fd), m_fd(-1),
+ m_bytes_consumed(0)
+{
+ if( !stats.exists() || !stats.has_access() ) {
+ setstate_impl( iostate::failbit ); // Note: conforming with std::ifstream open
+ DBG_PRINT("ByteOutStream_File::ctor: Error, not an existing or accessible file in %s, %s", stats.to_string().c_str(), to_string().c_str());
+ } else {
+ m_fd = ::dup(fd);
+ if ( 0 > m_fd ) {
+ setstate_impl( iostate::failbit ); // Note: conforming with std::ifstream open
+ DBG_PRINT("ByteOutStream_File::ctor: Error occurred in %s, %s", stats.to_string().c_str(), to_string().c_str());
+ }
+ // open file-descriptor is appending anyways
+ }
+}
+
+ByteOutStream_File::ByteOutStream_File(const int dirfd, const std::string& path, const jau::fs::fmode_t mode) noexcept
+: stats(), m_fd(-1),
+ m_bytes_consumed(0)
+{
+ if( jau::io::uri_tk::is_local_file_protocol(path) ) {
+ // cut of leading `file://`
+ std::string path2 = path.substr(7);
+ stats = jau::fs::file_stats(dirfd, path2);
+ } else {
+ stats = jau::fs::file_stats(dirfd, path);
+ }
+ if( ( stats.exists() && !stats.is_file() && !stats.has_fd() ) || !stats.has_access() ) {
+ setstate_impl( iostate::failbit ); // Note: conforming with std::ofstream open (?)
+ DBG_PRINT("ByteOutStream_File::ctor: Error, an existing non[file, fd] or not accessible element in %s, %s", stats.to_string().c_str(), to_string().c_str());
+ } else {
+ // O_NONBLOCK, is useless on files and counter to this class logic
+ if( stats.has_fd() ) {
+ m_fd = ::dup( stats.fd() );
+ } else {
+ const int dst_flags = ( stats.exists() ? 0 : O_CREAT|O_EXCL ) | O_WRONLY|O_BINARY|O_NOCTTY;
+ m_fd = __posix_openat64(dirfd, stats.path().c_str(), dst_flags, jau::fs::posix_protection_bits( mode ));
+ }
+ if ( 0 > m_fd ) {
+ setstate_impl( iostate::failbit ); // Note: conforming with std::ifstream open
+ DBG_PRINT("ByteOutStream_File::ctor: Error while opening %s, %s", stats.to_string().c_str(), to_string().c_str());
+ }
+ if( stats.is_file() ) {
+ off64_t abs_pos = __posix_lseek64(m_fd, 0, SEEK_END);
+ if( 0 > abs_pos ) {
+ setstate_impl( iostate::failbit );
+ ERR_PRINT("Failed to position existing file to end %s, errno %d %s",
+ to_string().c_str(), errno, strerror(errno));
+ }
+ }
+ }
+}
+
+ByteOutStream_File::ByteOutStream_File(const std::string& path, const jau::fs::fmode_t mode) noexcept
+: ByteOutStream_File(AT_FDCWD, path, mode) {}
+
+void ByteOutStream_File::close() noexcept {
+ if( 0 <= m_fd ) {
+ ::close(m_fd);
+ m_fd = -1;
+ }
+}
+
+std::string ByteOutStream_File::to_string() const noexcept {
+ return "ByteOutStream_File[consumed "+jau::to_decstring(m_bytes_consumed)+
+ ", fd "+std::to_string(m_fd)+
+ ", iostate["+jau::io::to_string(rdstate())+
+ "], "+stats.to_string()+
+ "]";
+}
+
diff --git a/test/java/jau/test/fs/TestFileUtils01.java b/test/java/jau/test/fs/TestFileUtils01.java
index 86e2191..7fdee33 100644
--- a/test/java/jau/test/fs/TestFileUtils01.java
+++ b/test/java/jau/test/fs/TestFileUtils01.java
@@ -24,9 +24,6 @@
package jau.test.fs;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
@@ -37,6 +34,7 @@ import org.jau.fs.FMode;
import org.jau.fs.FileStats;
import org.jau.fs.FileUtil;
import org.jau.fs.TraverseOptions;
+import org.jau.io.ByteOutStream_File;
import org.jau.io.PrintUtil;
import org.junit.Assert;
import org.junit.FixMethodOrder;
@@ -827,11 +825,10 @@ public class TestFileUtils01 extends FileUtilBaseTest {
test_file_stat_fd_item(FMode.Bit.chr, 1, fd_stdout_1, fd_stdout_l);
test_file_stat_fd_item(FMode.Bit.chr, 2, fd_stderr_1, fd_stderr_l);
try {
- final File file = new File("test07_file_stat_fd_tmp");
- final FileOutputStream fos = new FileOutputStream(file);
- final int fd = FileUtil.from_java_fd(fos.getFD());
+ final ByteOutStream_File fos = new ByteOutStream_File("test07_file_stat_fd_tmp", FMode.def_file);
+ final int fd = fos.fd();
final String named_fd = FileUtil.to_named_fd(fd);
- PrintUtil.fprintf_td(System.err, "XXXX.0: %s -> fd %d, named_fd '%s'\n", file.toString(), fd, named_fd);
+ PrintUtil.fprintf_td(System.err, "XXXX.0: %s -> fd %d, named_fd '%s'\n", fos.toString(), fd, named_fd);
Assert.assertTrue( 0 <= fd );
test_file_stat_fd_item(FMode.Bit.file, fd, FileUtil.to_named_fd(fd), "");
fos.close();
diff --git a/test/java/jau/test/io/TestByteStream01.java b/test/java/jau/test/io/TestByteStream01.java
index 77ff878..d4d3402 100644
--- a/test/java/jau/test/io/TestByteStream01.java
+++ b/test/java/jau/test/io/TestByteStream01.java
@@ -24,12 +24,7 @@
package jau.test.io;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -37,13 +32,17 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.jau.fs.FMode;
+import org.jau.fs.FileStats;
import org.jau.fs.FileUtil;
+import org.jau.fs.TraverseOptions;
import org.jau.io.Buffers;
import org.jau.io.ByteInStream;
import org.jau.io.ByteInStreamUtil;
import org.jau.io.ByteInStream_Feed;
import org.jau.io.ByteInStream_File;
import org.jau.io.ByteInStream_URL;
+import org.jau.io.ByteOutStream_File;
import org.jau.io.PrintUtil;
import org.jau.io.UriTk;
import org.junit.AfterClass;
@@ -69,26 +68,26 @@ public class TestByteStream01 extends JunitTracer {
static List<Long> fname_payload_size_lst = new ArrayList<Long>();
static boolean file_exists(final String name) {
- final File file = new File( name );
- return file.exists();
+ final FileStats stats = new FileStats( name );
+ return stats.is_file();
}
static long file_size(final String name) {
- try (ByteInStream_File f = new ByteInStream_File(name)) {
- return f.content_size();
- }
+ final FileStats stats = new FileStats( name );
+ return stats.size();
}
static boolean remove_file(final String name) {
- final File file = new File( name );
+ final FileStats stats = new FileStats( name );
try {
- if( file.exists() ) {
- if( !file.delete() ) {
+ if( stats.is_file() ) {
+ if( !FileUtil.remove(name, TraverseOptions.none) ) {
PrintUtil.println(System.err, "Remove.1: Failed deletion of existing file "+name);
return false;
}
+ return true;
}
- return true;
+ return !stats.exists();
} catch (final Exception ex) {
PrintUtil.println(System.err, "Remove.2: Failed deletion of existing file "+name+": "+ex.getMessage());
ex.printStackTrace();
@@ -96,7 +95,7 @@ public class TestByteStream01 extends JunitTracer {
return false;
}
- static void add_test_file(final String name, final long size_limit) {
+ static boolean add_test_file(final String name, final long size_limit) {
Assert.assertTrue( remove_file(name) );
Assert.assertTrue( remove_file(name+".enc") );
Assert.assertTrue( remove_file(name+".enc.dec") );
@@ -106,41 +105,35 @@ public class TestByteStream01 extends JunitTracer {
final Charset charset = Charset.forName("ASCII");
final byte[] one_line_bytes = one_line.getBytes(charset);
- final File file = new File( name );
- OutputStream out = null;
- try {
- Assert.assertFalse( file.exists() );
-
- out = new FileOutputStream(file);
-
- for(size=0; size < size_limit; size+=one_line_bytes.length) {
- out.write( one_line_bytes );
- }
- out.write( (byte)'X' ); // make it odd
- size += 1;
-
- } catch (final Exception ex) {
- PrintUtil.println(System.err, "Write test file: Failed "+name+": "+ex.getMessage());
- ex.printStackTrace();
- } finally {
- try {
- if( null != out ) {
- out.close();
+ Assert.assertFalse( file_exists(name) );
+ final ByteOutStream_File out = new ByteOutStream_File(name, FMode.def_file);
+ Assert.assertTrue( out.good() );
+ Assert.assertTrue( out.is_open() );
+ {
+ final int line_len = one_line_bytes.length;
+ for(size=0; size < size_limit; size+=line_len) {
+ if( line_len != out.write( one_line_bytes, 0, line_len ) ) {
+ PrintUtil.fprintf_td(System.err, "Write %d bytes to test file failed: %s", line_len, out.toString());
+ return false;
}
- } catch (final IOException e) {
- e.printStackTrace();
}
+ if( 1 != out.write( one_line_bytes, 0, 1 ) ) { // make it odd
+ PrintUtil.fprintf_td(System.err, "Write %d bytes to test file failed: %s", 1, out.toString());
+ return false;
+ }
+ size += 1;
}
-
}
fname_payload_lst.add(name);
fname_payload_copy_lst.add(name+".copy");
fname_payload_size_lst.add( Long.valueOf(size) );
+ return true;
}
static {
- add_test_file("test_cipher_01_11kiB.bin", 1024*11);
- add_test_file("test_cipher_02_65MiB.bin", 1024*1024*65);
+ PlatformRuntime.checkInitialized();
+ Assert.assertTrue( add_test_file("test_cipher_01_11kiB.bin", 1024*11) );
+ Assert.assertTrue( add_test_file("test_cipher_02_65MiB.bin", 1024*1024*65) );
}
static boolean system(final String[] command) {
@@ -195,54 +188,38 @@ public class TestByteStream01 extends JunitTracer {
PrintUtil.fprintf_td(System.err, "Transfer Start: %s%n", input);
remove_file(output_fname);
- final File file = new File( output_fname );
- if( file.exists() ) {
- return false;
- }
-
- final OutputStream out[] = { null };
- try {
- Assert.assertFalse( file.exists() );
- out[0] = new FileOutputStream(file);
- } catch (final Exception ex) {
- PrintUtil.fprintf_td(System.err, "Opening output file : Failed %s: %s%n", file, ex.getMessage());
- ex.printStackTrace();
+ if( file_exists( output_fname ) ) {
return false;
}
-
final long[] out_bytes_payload = { 0 };
- final boolean[] out_failure = { false };
- final ByteInStreamUtil.StreamConsumer1 consumer = (final byte[] data, final int data_len, final boolean is_final) -> {
- try {
+ try ( ByteOutStream_File out = new ByteOutStream_File(output_fname, FMode.def_file) ) {
+ // final ByteOutStream_File[] out = { _out };
+ Assert.assertTrue( out.good() );
+
+ final ByteInStreamUtil.StreamConsumer1 consumer = (final byte[] data, final int data_len, final boolean is_final) -> {
if( !is_final && ( !input.has_content_size() || out_bytes_payload[0] + data_len < input.content_size() ) ) {
- out[0].write( data, 0, data_len );
- out_bytes_payload[0] += data_len;
- return true; // continue ..
+ final int written = out.write( data, 0, data_len );
+ out_bytes_payload[0] += written;
+ return data_len == written; // continue ..
} else {
- out[0].write( data, 0, data_len );
- out_bytes_payload[0] += data_len;
+ final int written = out.write( data, 0, data_len );
+ out_bytes_payload[0] += written;
return false; // EOS
}
- } catch (final Exception ex) {
- PrintUtil.fprintf_td(System.err, "Write input bytes: Failed %s: %s%n", input, ex.getMessage());
- ex.printStackTrace();
- out_failure[0] = true;
- return false;
- }
- };
- final byte[] io_buffer = new byte[buffer_size];
- final long in_bytes_total = ByteInStreamUtil.read_stream(input, io_buffer, consumer);
- input.closeStream();
- try {
- out[0].close();
- out[0] = null;
- } catch (final IOException e) {
- e.printStackTrace();
- }
+ };
+ final byte[] io_buffer = new byte[buffer_size];
+ final long in_bytes_total = ByteInStreamUtil.read_stream(input, io_buffer, consumer);
+ input.closeStream();
+ out.closeStream();
- if ( 0==in_bytes_total || out_failure[0] ) {
- PrintUtil.fprintf_td(System.err, "ByteStream copy failed: Output file write failed %s%n", output_fname);
- return false;
+ if ( 0==in_bytes_total || input.fail() ) {
+ PrintUtil.fprintf_td(System.err, "ByteStream copy failed: Input file read failed in %s, out %s%n", input, out);
+ return false;
+ }
+ if ( out.fail() ) {
+ PrintUtil.fprintf_td(System.err, "ByteStream copy failed: Output file write failed in %s, out %s%n", input, out);
+ return false;
+ }
}
final long _td = org.jau.sys.Clock.currentTimeMillis() - _t0;
@@ -257,65 +234,40 @@ public class TestByteStream01 extends JunitTracer {
PrintUtil.fprintf_td(System.err, "Transfer Start: %s%n", input);
remove_file(output_fname);
- final File file = new File( output_fname );
- if( file.exists() ) {
+ if( file_exists( output_fname ) ) {
return false;
}
-
- final OutputStream out[] = { null };
- final FileChannel outc[] = { null };
- try {
- Assert.assertFalse( file.exists() );
- out[0] = new FileOutputStream(file);
- outc[0] = ((FileOutputStream)out[0]).getChannel();
- } catch (final Exception ex) {
- PrintUtil.fprintf_td(System.err, "Opening output file : Failed %s: %s%n", file, ex.getMessage());
- ex.printStackTrace();
- if( null != outc[0] ) {
- try { outc[0].close(); } catch (final IOException e) { }
- }
- if( null != out[0] ) {
- try { out[0].close(); } catch (final IOException e) { }
- }
- return false;
- }
-
final long[] out_bytes_payload = { 0 };
- final boolean[] out_failure = { false };
- final ByteInStreamUtil.StreamConsumer2 consumer = (final ByteBuffer data, final boolean is_final) -> {
- try {
+ try ( ByteOutStream_File out = new ByteOutStream_File(output_fname, FMode.def_file) ) {
+ Assert.assertTrue( out.good() );
+
+ final ByteInStreamUtil.StreamConsumer2 consumer = (final ByteBuffer data, final boolean is_final) -> {
final int data_len = data.remaining();
if( !is_final && ( !input.has_content_size() || out_bytes_payload[0] + data_len < input.content_size() ) ) {
- outc[0].write(data);
+ final int written = out.write(data);
data.rewind();
- out_bytes_payload[0] += data_len;
- return true; // continue ..
+ out_bytes_payload[0] += written;
+ return written == data_len; // continue ..
} else {
- outc[0].write(data);
+ final int written = out.write(data);
data.rewind();
- out_bytes_payload[0] += data_len;
+ out_bytes_payload[0] += written;
return false; // EOS
}
- } catch (final Exception ex) {
- PrintUtil.fprintf_td(System.err, "Write input bytes: Failed %s: %s%n", input, ex.getMessage());
- ex.printStackTrace();
- out_failure[0] = true;
- return false;
- }
- };
- final ByteBuffer io_buffer = Buffers.newDirectByteBuffer(buffer_size);
- final long in_bytes_total = ByteInStreamUtil.read_stream(input, io_buffer, consumer);
- input.closeStream();
- if( null != outc[0] ) {
- try { outc[0].close(); outc[0]=null; } catch (final IOException e) { e.printStackTrace(); }
- }
- if( null != out[0] ) {
- try { out[0].close(); out[0]=null; } catch (final IOException e) { e.printStackTrace(); }
- }
+ };
+ final ByteBuffer io_buffer = Buffers.newDirectByteBuffer(buffer_size);
+ final long in_bytes_total = ByteInStreamUtil.read_stream(input, io_buffer, consumer);
+ input.closeStream();
+ out.closeStream();
- if ( 0==in_bytes_total || out_failure[0] ) {
- PrintUtil.fprintf_td(System.err, "ByteStream copy failed: Output file write failed %s%n", output_fname);
- return false;
+ if ( 0==in_bytes_total || input.fail() ) {
+ PrintUtil.fprintf_td(System.err, "ByteStream copy failed: Input file read failed in %s, out %s%n", input, out);
+ return false;
+ }
+ if ( out.fail() ) {
+ PrintUtil.fprintf_td(System.err, "ByteStream copy failed: Output file write failed in %s, out %s%n", input, out);
+ return false;
+ }
}
final long _td = org.jau.sys.Clock.currentTimeMillis() - _t0;
@@ -388,7 +340,7 @@ public class TestByteStream01 extends JunitTracer {
try { Thread.sleep(100); } catch (final Throwable t) {} // time to read 404 response
PrintUtil.fprintf_td(System.err, "test00_protocols: not_exiting_http_uri: %s%n", in);
Assert.assertTrue( in.end_of_data() );
- Assert.assertTrue( in.error() );
+ Assert.assertTrue( in.fail() );
Assert.assertEquals( 0, in.content_size() );
} else {
Assert.assertNull(in);
@@ -414,7 +366,7 @@ public class TestByteStream01 extends JunitTracer {
PrintUtil.fprintf_td(System.err, "test00_protocols: local-file-0: %s%n", in);
}
Assert.assertNotEquals( null, in );
- Assert.assertFalse( in.error() );
+ Assert.assertFalse( in.fail() );
final boolean res = transfer_nio(in, fname_payload_copy_lst.get(file_idx), 4096);
Assert.assertTrue( res );
@@ -436,7 +388,7 @@ public class TestByteStream01 extends JunitTracer {
PrintUtil.fprintf_td(System.err, "test00_protocols: local-file-1: %s%n", in);
}
Assert.assertNotNull( in );
- Assert.assertFalse( in.error() );
+ Assert.assertFalse( in.fail() );
final boolean res = transfer_nio(in, fname_payload_copy_lst.get(file_idx), 4096);
Assert.assertTrue( res );
@@ -459,7 +411,7 @@ public class TestByteStream01 extends JunitTracer {
}
if( http_support_expected ) {
Assert.assertNotNull( in );
- Assert.assertFalse( in.error() );
+ Assert.assertFalse( in.fail() );
final boolean res = transfer_nio(in, fname_payload_copy_lst.get(file_idx), 4096);
Assert.assertTrue( res );
@@ -617,7 +569,7 @@ public class TestByteStream01 extends JunitTracer {
Assert.assertTrue( file_exists( fname_payload_copy_lst.get(file_idx) ) );
final long copy_size = file_size(fname_payload_copy_lst.get(file_idx));
- Assert.assertTrue( data_stream.error() );
+ Assert.assertTrue( data_stream.fail() );
Assert.assertFalse( data_stream.has_content_size() );
Assert.assertEquals( data_stream.content_size(), 0 );
Assert.assertEquals( 0, copy_size );
@@ -962,7 +914,7 @@ public class TestByteStream01 extends JunitTracer {
try {
feeder_thread.join(1000);
} catch (final InterruptedException e) { }
- Assert.assertTrue( res );
+ Assert.assertFalse( res );
Assert.assertTrue( file_exists( fname_payload_copy_lst.get(file_idx) ) );
final long copy_size = file_size(fname_payload_copy_lst.get(file_idx));
@@ -980,7 +932,7 @@ public class TestByteStream01 extends JunitTracer {
try {
feeder_thread.join(1000);
} catch (final InterruptedException e) { }
- Assert.assertTrue( res );
+ Assert.assertFalse( res );
Assert.assertTrue( file_exists( fname_payload_copy_lst.get(file_idx) ) );
final long copy_size = file_size(fname_payload_copy_lst.get(file_idx));
diff --git a/test/test_bytestream01.cpp b/test/test_bytestream01.cpp
index dac6a28..5b107b8 100644
--- a/test/test_bytestream01.cpp
+++ b/test/test_bytestream01.cpp
@@ -22,14 +22,10 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-#include <iostream>
#include <cassert>
#include <cinttypes>
#include <cstring>
-#include <fstream>
-#include <iostream>
-
#include <jau/test/catch2_ext.hpp>
#include <jau/debug.hpp>
@@ -56,7 +52,7 @@ class TestByteStream01 {
class data {
private:
- static void add_test_file(const std::string name, const size_t size_limit) {
+ static bool add_test_file(const std::string name, const size_t size_limit) {
jau::fs::remove(name);
jau::fs::remove(name+".enc");
jau::fs::remove(name+".enc.dec");
@@ -64,24 +60,31 @@ class TestByteStream01 {
size_t size;
{
static const std::string one_line = "Hello World, this is a test and I like it. Exactly 100 characters long. 0123456780 abcdefghjklmnop..";
- std::ofstream ofs(name, std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File ofs(name);
REQUIRE( ofs.good() == true );
REQUIRE( ofs.is_open() == true );
for(size=0; size < size_limit; size+=one_line.size()) {
- ofs.write(reinterpret_cast<const char*>(one_line.data()), one_line.size());
+ if( one_line.size() != ofs.write(one_line.data(), one_line.size()) ) {
+ ERR_PRINT("Write %zu bytes to test file failed: %s", one_line.size(), ofs.to_string().c_str());
+ return false;
+ }
+ }
+ if( 1 != ofs.write("X", 1) ) { // make it odd
+ ERR_PRINT("Write %zu bytes to test file failed: %s", 1, ofs.to_string().c_str());
+ return false;
}
- ofs.write("X", 1); // make it odd
size += 1;
}
fname_payload_lst.push_back(name);
fname_payload_copy_lst.push_back(name+".copy");
fname_payload_size_lst.push_back( size );
+ return true;
}
data() {
- add_test_file("testfile_blob_01_11kiB.bin", 1024*11);
- add_test_file("testfile_blob_02_65MiB.bin", 1024*1024*65);
+ REQUIRE( true == add_test_file("testfile_blob_01_11kiB.bin", 1024*11) );
+ REQUIRE( true == add_test_file("testfile_blob_02_65MiB.bin", 1024*1024*65) );
}
public:
static const data& get() {
@@ -132,7 +135,7 @@ class TestByteStream01 {
}
}
}
- std::ofstream outfile(output_fname, std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File outfile(output_fname);
if ( !outfile.good() || !outfile.is_open() ) {
ERR_PRINT2("ByteStream copy failed: Couldn't open output file %s", output_fname.c_str());
return false;
@@ -141,12 +144,12 @@ class TestByteStream01 {
uint64_t out_bytes_payload = 0;
jau::io::StreamConsumerFunc consume_data = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) -> bool {
if( !is_final && ( !input.has_content_size() || out_bytes_payload + data.size() < input.content_size() ) ) {
- outfile.write(reinterpret_cast<char*>(data.data()), data.size());
- out_bytes_payload += data.size();
- return true; // continue ..
+ const size_t written = outfile.write(data.data(), data.size());
+ out_bytes_payload += written;
+ return data.size() == written; // continue ..
} else {
- outfile.write(reinterpret_cast<char*>(data.data()), data.size());
- out_bytes_payload += data.size();
+ const size_t written = outfile.write(data.data(), data.size());
+ out_bytes_payload += written;
return false; // EOS
}
};
@@ -155,8 +158,12 @@ class TestByteStream01 {
const uint64_t in_bytes_total = jau::io::read_stream(input, io_buffer, consume_data);
input.close();
- if ( 0==in_bytes_total || outfile.fail() ) {
- IRQ_PRINT("ByteStream copy failed: Output file write failed %s", output_fname.c_str());
+ if ( 0==in_bytes_total || input.fail() ) {
+ IRQ_PRINT("ByteStream copy failed: Input file read failed in %s, out %s", input.to_string().c_str(), outfile.to_string().c_str());
+ return false;
+ }
+ if ( outfile.fail() ) {
+ IRQ_PRINT("ByteStream copy failed: Output file write failed in %s, out %s", input.to_string().c_str(), outfile.to_string().c_str());
return false;
}
@@ -226,7 +233,7 @@ class TestByteStream01 {
jau::sleep_for( 100_ms ); // time to read 404 response
jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_http_uri: %s", in->to_string().c_str());
REQUIRE( true == in->end_of_data() );
- REQUIRE( true == in->error() );
+ REQUIRE( true == in->fail() );
REQUIRE( 0 == in->content_size() );
} else {
REQUIRE( nullptr == in );
@@ -250,7 +257,7 @@ class TestByteStream01 {
jau::PLAIN_PRINT(true, "test00_protocols: local-file-0: %s", in->to_string().c_str());
}
REQUIRE( nullptr != in );
- REQUIRE( false == in->error() );
+ REQUIRE( false == in->fail() );
bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
REQUIRE( true == res );
@@ -274,7 +281,7 @@ class TestByteStream01 {
jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: NULL from url '%s'", url.c_str());
}
REQUIRE( nullptr != in );
- REQUIRE( false == in->error() );
+ REQUIRE( false == in->fail() );
bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
REQUIRE( true == res );
@@ -297,7 +304,7 @@ class TestByteStream01 {
}
if( http_support_expected ) {
REQUIRE( nullptr != in );
- REQUIRE( false == in->error() );
+ REQUIRE( false == in->fail() );
bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
REQUIRE( true == res );
@@ -425,7 +432,7 @@ class TestByteStream01 {
jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
REQUIRE( true == out_stats.exists() );
REQUIRE( true == out_stats.is_file() );
- REQUIRE( data_stream.error() == true );
+ REQUIRE( data_stream.fail() == true );
REQUIRE( data_stream.has_content_size() == false );
REQUIRE( data_stream.content_size() == 0 );
REQUIRE( 0 == out_stats.size() );
@@ -503,7 +510,7 @@ class TestByteStream01 {
}
}
// probably set after transfering due to above sleep, which also ends when total size has been reached.
- data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
+ // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
}
// full speed, with content size, interrupting 1/4 way
@@ -525,7 +532,7 @@ class TestByteStream01 {
}
}
// probably set after transfering due to above sleep, which also ends when total size has been reached.
- data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
+ // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
}
void test20_copy_fed_ok_buff4k_feed1k() {
@@ -717,7 +724,7 @@ class TestByteStream01 {
if( feeder_thread.joinable() ) {
feeder_thread.join();
}
- REQUIRE( true == res );
+ REQUIRE( false == res );
jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
REQUIRE( true == out_stats.exists() );
@@ -736,7 +743,7 @@ class TestByteStream01 {
if( feeder_thread.joinable() ) {
feeder_thread.join();
}
- REQUIRE( true == res );
+ REQUIRE( false == res );
jau::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
REQUIRE( true == out_stats.exists() );
diff --git a/test/test_fileutils01.cpp b/test/test_fileutils01.cpp
index 4629ad6..0680f6e 100644
--- a/test/test_fileutils01.cpp
+++ b/test/test_fileutils01.cpp
@@ -31,8 +31,6 @@ extern "C" {
#include <sys/wait.h>
#include <unistd.h>
}
-#include <fstream>
-#include <iostream>
class TestFileUtil01 : TestFileUtilBase {
public:
@@ -858,9 +856,9 @@ class TestFileUtil01 : TestFileUtilBase {
jau::fprintf_td(stderr, "Child: Error: stats_stdout %s\n", stats_stdout.to_string().c_str());
::_exit(EXIT_FAILURE);
}
- std::ofstream outfile(fd_stdout, std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File outfile(fd_stdout);
if( !outfile.good() || !outfile.is_open() ) {
- jau::fprintf_td(stderr, "Child: Error: outfile bad\n");
+ jau::fprintf_td(stderr, "Child: Error: outfile bad: %s\n", outfile.to_string().c_str());
::_exit(EXIT_FAILURE);
}
@@ -880,7 +878,7 @@ class TestFileUtil01 : TestFileUtilBase {
::close(pipe_fds[1]);
if( outfile.fail() ) {
- jau::fprintf_td(stderr, "Child: Error: outfile failed after write/closure\n");
+ jau::fprintf_td(stderr, "Child: Error: outfile failed after write/closure: %s\n", outfile.to_string().c_str());
::_exit(EXIT_FAILURE);
}
jau::fprintf_td(stderr, "Child: Done\n");
@@ -909,7 +907,7 @@ class TestFileUtil01 : TestFileUtilBase {
// capture stdin
jau::io::ByteInStream_File infile(fd_stdin);
jau::fprintf_td(stderr, "Parent: infile %s\n", infile.to_string().c_str());
- REQUIRE( !infile.error() );
+ REQUIRE( !infile.fail() );
uint8_t buffer[pipe_msg_count * pipe_msg_len + 512];
::bzero(buffer, sizeof(buffer));
@@ -918,7 +916,7 @@ class TestFileUtil01 : TestFileUtilBase {
while( !infile.end_of_data() && total_read < sizeof(buffer) ) {
const size_t got = infile.read(buffer+total_read, sizeof(buffer)-total_read);
jau::fprintf_td(stderr, "Parent: infile.a_ %s\n", infile.to_string().c_str());
- REQUIRE( !infile.error() );
+ REQUIRE( !infile.fail() );
total_read += got;
jau::fprintf_td(stderr, "Parent: Got %zu -> %zu\n", got, total_read);
}
@@ -927,7 +925,7 @@ class TestFileUtil01 : TestFileUtilBase {
jau::fprintf_td(stderr, "Parent: infile.a_2 %s\n", infile.to_string().c_str());
::close(pipe_fds[0]);
jau::fprintf_td(stderr, "Parent: infile.a_3 %s\n", infile.to_string().c_str());
- REQUIRE( !infile.error() );
+ REQUIRE( !infile.fail() );
}
// check actual transmitted content
REQUIRE( total_read == pipe_msg_len*pipe_msg_count);
diff --git a/test/test_iostream01.cpp b/test/test_iostream01.cpp
index ec2a50a..345f029 100644
--- a/test/test_iostream01.cpp
+++ b/test/test_iostream01.cpp
@@ -23,14 +23,10 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-#include <iostream>
#include <cassert>
#include <cinttypes>
#include <cstring>
-#include <fstream>
-#include <iostream>
-
#include <thread>
#include <pthread.h>
@@ -38,6 +34,7 @@
#include <jau/file_util.hpp>
#include <jau/io_util.hpp>
+#include <jau/byte_stream.hpp>
#include <jau/debug.hpp>
@@ -60,13 +57,13 @@ class TestIOStream01 {
jau::fs::remove(basename_10kiB);
{
std::string one_line = "Hello World, this is a test and I like it. Exactly 100 characters long. 0123456780 abcdefghjklmnop..";
- std::ofstream ofs(basename_10kiB, std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File ofs(basename_10kiB);
REQUIRE( ofs.good() == true );
REQUIRE( ofs.is_open() == true );
for(int i=0; i < 1024*10; i+=one_line.size()) { // 10kiB
- ofs.write(reinterpret_cast<char*>(one_line.data()), one_line.size());
+ REQUIRE( one_line.size() == ofs.write(one_line.data(), one_line.size()) );
}
}
if( jau::io::uri_tk::protocol_supported("http:") ) {
@@ -163,7 +160,7 @@ class TestIOStream01 {
const size_t file_size = in_stats.size();
const std::string url_input = url_input_root + basename_10kiB;
- std::ofstream outfile("testfile01_01_out.bin", std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File outfile("testfile01_01_out.bin");
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );
@@ -173,7 +170,7 @@ class TestIOStream01 {
jau::io::StreamConsumerFunc consume = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) noexcept -> bool {
consumed_calls++;
consumed_total_bytes += data.size();
- outfile.write(reinterpret_cast<char*>(data.data()), data.size());
+ outfile.write(data.data(), data.size());
jau::PLAIN_PRINT(true, "test01_sync_ok #%zu: consumed size %zu, total %" PRIu64 ", capacity %zu, final %d",
consumed_calls, data.size(), consumed_total_bytes, data.capacity(), is_final );
return true;
@@ -194,7 +191,7 @@ class TestIOStream01 {
}
const std::string url_input = url_input_root + "doesnt_exists.txt";
- std::ofstream outfile("testfile02_01_out.bin", std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File outfile("testfile02_01_out.bin");
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );
@@ -204,7 +201,7 @@ class TestIOStream01 {
jau::io::StreamConsumerFunc consume = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) noexcept -> bool {
consumed_calls++;
consumed_total_bytes += data.size();
- outfile.write(reinterpret_cast<char*>(data.data()), data.size());
+ outfile.write(data.data(), data.size());
jau::PLAIN_PRINT(true, "test02_sync_404 #%zu: consumed size %zu, total %" PRIu64 ", capacity %zu, final %d",
consumed_calls, data.size(), consumed_total_bytes, data.capacity(), is_final );
return true;
@@ -227,7 +224,7 @@ class TestIOStream01 {
const size_t file_size = in_stats.size();
const std::string url_input = url_input_root + basename_10kiB;
- std::ofstream outfile("testfile11_01_out.bin", std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File outfile("testfile11_01_out.bin");
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );
@@ -252,7 +249,7 @@ class TestIOStream01 {
consumed_total_bytes += consumed_bytes;
jau::PLAIN_PRINT(true, "test11_async_ok.0 #%zu: consumed[this %zu, total %" PRIu64 ", result %d, rb %s",
consumed_loops, consumed_bytes, consumed_total_bytes, result.load(), rb.toString().c_str() );
- outfile.write(reinterpret_cast<char*>(buffer.data()), consumed_bytes);
+ outfile.write(buffer.data(), consumed_bytes);
}
const uint64_t out_bytes_total = outfile.tellp();
jau::PLAIN_PRINT(true, "test11_async_ok.X Done: total %" PRIu64 ", result %d, rb %s",
@@ -275,7 +272,7 @@ class TestIOStream01 {
}
const std::string url_input = url_input_root + "doesnt_exists.txt";
- std::ofstream outfile("testfile12_01_out.bin", std::ios::out | std::ios::binary);
+ jau::io::ByteOutStream_File outfile("testfile12_01_out.bin");
REQUIRE( outfile.good() );
REQUIRE( outfile.is_open() );