Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
TextToBinary.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "InternallySynchronizedOutputStream.h"
5
6namespace Stroika::Foundation::Streams::TextToBinary {
7
8 namespace Private_ {
9 class UnSeekable_CodeCvt_Rep_ final : public OutputStream::IRep<Character> {
10 public:
11 UnSeekable_CodeCvt_Rep_ (const OutputStream::Ptr<byte>& src, const Characters::CodeCvt<Character>& converter)
12 : _fSource{src}
13 , _fConverter{converter}
14 {
15 RequireNotNull (src);
16 }
17
18 protected:
19 virtual bool IsSeekable () const override
20 {
21 return false;
22 }
23 virtual void CloseWrite () override
24 {
25 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
26 if (IsOpenWrite ()) {
27 _fSource.Close ();
28 }
29 Ensure (not IsOpenWrite ());
30 }
31 virtual bool IsOpenWrite () const override
32 {
33 return _fSource.IsOpen ();
34 }
35 virtual SeekOffsetType GetWriteOffset () const override
36 {
38 Require (IsOpenWrite ());
39 return 0;
40 }
41 virtual SeekOffsetType SeekWrite (Whence /*whence*/, SignedSeekOffsetType /*offset*/) override
42 {
43 AssertNotImplemented (); // not seekable
44 Require (IsOpenWrite ());
45 return 0;
46 }
47 virtual void Write (span<const Character> elts) override
48 {
49 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
50 Require (IsOpenWrite ());
51 Memory::StackBuffer<byte> cvtBuf{elts.size () * 5}; // excessive but start with that
52 auto srcSpan = elts;
53 auto trgSpan = span<byte>{cvtBuf.data (), cvtBuf.size ()};
54 trgSpan = _fConverter.Characters2Bytes (srcSpan, trgSpan);
55 _fSource.Write (trgSpan);
56 }
57 virtual void Flush () override
58 {
59 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
60 Require (IsOpenWrite ());
61 _fSource.Flush ();
62 }
63
64 protected:
65 OutputStream::Ptr<byte> _fSource;
66 Characters::CodeCvt<Character> _fConverter;
67 std::mbstate_t _fMBState_{};
68 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
69 };
70
71 template <Characters ::IUNICODECanUnambiguouslyConvertFrom OUTPUT_CHAR_T>
72 class UnSeekable_UTFConverter_Rep_ final : public OutputStream::IRep<Character> {
73 public:
74 template <typename CONVERTER>
75 UnSeekable_UTFConverter_Rep_ (const OutputStream::Ptr<byte>& src, CONVERTER&& converter)
76 : _fSource{src}
77 , _fConverter{forward<CONVERTER> (converter)}
78 {
79 RequireNotNull (src);
80 }
81 UnSeekable_UTFConverter_Rep_ (const OutputStream::Ptr<byte>& src)
82 : _fSource{src}
83 , _fConverter{Characters::UTFConvert::kThe}
84 {
85 }
86
87 protected:
88 virtual bool IsSeekable () const override
89 {
90 return false;
91 }
92 virtual void CloseWrite () override
93 {
94 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
95 if (IsOpenWrite ()) {
96 _fSource.Close ();
97 }
98 Ensure (not IsOpenWrite ());
99 }
100 virtual bool IsOpenWrite () const override
101 {
102 return _fSource.IsOpen ();
103 }
104 virtual SeekOffsetType GetWriteOffset () const override
105 {
107 Require (IsOpenWrite ());
108 return 0;
109 }
110 virtual SeekOffsetType SeekWrite (Whence /*whence*/, SignedSeekOffsetType /*offset*/) override
111 {
112 AssertNotImplemented (); // not seekable
113 Require (IsOpenWrite ());
114 return 0;
115 }
116 virtual void Write (span<const Character> elts) override
117 {
118 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
119 Require (IsOpenWrite ());
120 auto srcSpan = elts;
121 Memory::StackBuffer<OUTPUT_CHAR_T> cvtBuf{_fConverter.ComputeTargetBufferSize<OUTPUT_CHAR_T> (srcSpan)};
122 auto trgSpan = span<OUTPUT_CHAR_T>{cvtBuf.data (), cvtBuf.size ()};
123 auto r = _fConverter.ConvertSpan (srcSpan, trgSpan);
124 auto trgBytes = as_bytes (r);
125 _fSource.Write (trgBytes);
126 }
127 virtual void Flush () override
128 {
129 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
130 Require (IsOpenWrite ());
131 _fSource.Flush ();
132 }
133
134 protected:
135 OutputStream::Ptr<byte> _fSource;
136 Characters::UTFConvert _fConverter;
137 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
138 };
139 }
140
141 /*
142 ********************************************************************************
143 ************************ TextToBinary::Writer::New *****************************
144 ********************************************************************************
145 */
146 namespace Writer {
147 inline auto New (const OutputStream::Ptr<Character>& src) -> OutputStream::Ptr<Character>
148 {
149 return src;
150 }
151 inline OutputStream::Ptr<Character> New (const OutputStream::Ptr<byte>& src, const Characters::CodeCvt<>& char2OutputConverter)
152 {
153 return OutputStream::Ptr<Character>{make_shared<Private_::UnSeekable_CodeCvt_Rep_> (src, char2OutputConverter)};
154 }
155 inline OutputStream::Ptr<Character> New (const OutputStream::Ptr<byte>& src, Characters::UnicodeExternalEncodings e, Characters::ByteOrderMark bom)
156 {
157 using Ptr = OutputStream::Ptr<Character>;
158 if (bom == Characters::ByteOrderMark::eInclude) {
159 src.Write (Characters::GetByteOrderMark (e));
160 }
161 // handle a few common cases more efficiently, without vectoring through CodeCvt<> (which has an extra level of indirection)
162 switch (e) {
163 case Characters::UnicodeExternalEncodings::eUTF8:
164 return Ptr{make_shared<Private_::UnSeekable_UTFConverter_Rep_<char8_t>> (src)};
165 case Characters::UnicodeExternalEncodings::eUTF16:
166 return Ptr{make_shared<Private_::UnSeekable_UTFConverter_Rep_<char16_t>> (src)};
167 case Characters::UnicodeExternalEncodings::eUTF32:
168 return Ptr{make_shared<Private_::UnSeekable_UTFConverter_Rep_<char32_t>> (src)};
169 default:
170 // but default to using the CodeCvt writer
171 return New (src, Characters::CodeCvt<Character> (e));
172 }
173 }
174 template <typename... ARGS>
175 inline OutputStream::Ptr<Character> New (Execution::InternallySynchronized internallySynchronized, ARGS... args)
176 {
177 switch (internallySynchronized) {
178 case Execution::eNotKnownInternallySynchronized:
179 return New (forward<ARGS...> (args...));
180 case Execution::eInternallySynchronized:
181 // @todo could explicitly specialize more cases and handle more efficiently, but using the REP overload of InternallySynchronizedInputStream
182 return InternallySynchronizedOutputStream::New ({}, New (forward<ARGS...> (args...)));
183 }
184 }
185 }
186
187}
#define AssertNotImplemented()
Definition Assertions.h:401
#define RequireNotNull(p)
Definition Assertions.h:347
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
virtual void Write(span< const ELEMENT_TYPE > elts)=0
constexpr span< const byte > GetByteOrderMark(UnicodeExternalEncodings e) noexcept
UnicodeExternalEncodings
list of external UNICODE character encodings, for file IO (eDEFAULT = eUTF8)
Definition UTFConvert.h:31
Streams::OutputStream::Ptr< Character > Ptr
TextToBinary::Writer wrap some sink (typically a binary stream), and produce a text sink you can Writ...