diff options
author | Sven Gothel <[email protected]> | 2022-08-05 17:58:06 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-08-05 17:58:06 +0200 |
commit | c71be85f2be7b9e07cf13ec93926bbf1f8ad8bed (patch) | |
tree | 17ccf05c2f89fc4394dd378b422e57a3a48eb29d | |
parent | f8bd6cbacb30c0c10dba47fa896e0f37e3d83272 (diff) |
Add ByteOutStream and ByteOutStream_File POSIX 'fd' implementation, ...
- Add ByteOutStream interface
- Add ByteOutStream_File implementation similar to ByteInStream_File, based on POSIX fd etc.
- Add is_open() to ByteInStream and ByteOutStream interface.
-rw-r--r-- | include/jau/byte_stream.hpp | 165 | ||||
-rw-r--r-- | java_base/org/jau/io/Buffers.java | 37 | ||||
-rw-r--r-- | java_jni/jni/CMakeLists.txt | 1 | ||||
-rw-r--r-- | java_jni/jni/jau/ByteInStream_Feed.cxx | 9 | ||||
-rw-r--r-- | java_jni/jni/jau/ByteInStream_File.cxx | 15 | ||||
-rw-r--r-- | java_jni/jni/jau/ByteInStream_URL.cxx | 10 | ||||
-rw-r--r-- | java_jni/jni/jau/ByteOutStream_File.cxx | 260 | ||||
-rw-r--r-- | java_jni/org/jau/io/ByteInStream.java | 38 | ||||
-rw-r--r-- | java_jni/org/jau/io/ByteInStream_Feed.java | 5 | ||||
-rw-r--r-- | java_jni/org/jau/io/ByteInStream_File.java | 3 | ||||
-rw-r--r-- | java_jni/org/jau/io/ByteInStream_URL.java | 5 | ||||
-rw-r--r-- | java_jni/org/jau/io/ByteOutStream.java | 105 | ||||
-rw-r--r-- | java_jni/org/jau/io/ByteOutStream_File.java | 188 | ||||
-rw-r--r-- | java_jni/org/jau/io/IOStateFunc.java | 59 | ||||
-rw-r--r-- | src/byte_stream.cpp | 114 | ||||
-rw-r--r-- | test/java/jau/test/fs/TestFileUtils01.java | 11 | ||||
-rw-r--r-- | test/java/jau/test/io/TestByteStream01.java | 222 | ||||
-rw-r--r-- | test/test_bytestream01.cpp | 61 | ||||
-rw-r--r-- | test/test_fileutils01.cpp | 14 | ||||
-rw-r--r-- | test/test_iostream01.cpp | 23 |
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() ); |