1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <soulng/util/BinaryWriter.hpp>
  7 #include <soulng/util/TextUtils.hpp>
  8 #include <soulng/util/Unicode.hpp>
  9  
 10 namespace soulng { namespace util {
 11 
 12 using namespace soulng::unicode;
 13 
 14 BinaryWriter::BinaryWriter(const std::string& fileName_) : fileName(fileName_)file(OpenWrite(fileName.c_str())fileNameLockKind::write)buffer()bufp(buffer)bufend(buffer + N)pos(0)
 15 {
 16     if (!file)
 17     {
 18         throw std::runtime_error("could not open '" + fileName + "' for writing: " + soulng::util::PlatformStringToUtf8(std::strerror(errno)));
 19     }
 20 }
 21 
 22 BinaryWriter::~BinaryWriter()
 23 {
 24     FlushBuffer();
 25 }
 26 
 27 void BinaryWriter::Write(bool x)
 28 {
 29     Write(uint8_t(x));
 30 }
 31 
 32 void BinaryWriter::Write(uint8_t x)
 33 {
 34     if (BufferFull())
 35     {
 36         FlushBuffer();
 37     }
 38     *bufp++ = x;
 39     ++pos;
 40 }
 41 
 42 void BinaryWriter::Write(int8_t x)
 43 {
 44     Write(static_cast<uint8_t>(x));
 45 }
 46 
 47 void BinaryWriter::Write(uint16_t x)
 48 {
 49     uint8_t b0 = static_cast<uint8_t>(x >> 8);
 50     uint8_t b1 = static_cast<uint8_t>(x);
 51     Write(b0);
 52     Write(b1);
 53 }
 54 
 55 void BinaryWriter::Write(int16_t x)
 56 {
 57     Write(static_cast<uint16_t>(x));
 58 }
 59 
 60 void BinaryWriter::Write(uint32_t x)
 61 {
 62     uint8_t b0 = static_cast<uint8_t>(x >> 24);
 63     uint8_t b1 = static_cast<uint8_t>(x >> 16);
 64     uint8_t b2 = static_cast<uint8_t>(x >> 8);
 65     uint8_t b3 = static_cast<uint8_t>(x);
 66     Write(b0);
 67     Write(b1);
 68     Write(b2);
 69     Write(b3);
 70 }
 71 
 72 void BinaryWriter::Write(int32_t x)
 73 {
 74     Write(static_cast<uint32_t>(x));
 75 }
 76 
 77 void BinaryWriter::Write(uint64_t x)
 78 {
 79     uint8_t b0 = static_cast<uint8_t>(x >> 56);
 80     uint8_t b1 = static_cast<uint8_t>(x >> 48);
 81     uint8_t b2 = static_cast<uint8_t>(x >> 40);
 82     uint8_t b3 = static_cast<uint8_t>(x >> 32);
 83     uint8_t b4 = static_cast<uint8_t>(x >> 24);
 84     uint8_t b5 = static_cast<uint8_t>(x >> 16);
 85     uint8_t b6 = static_cast<uint8_t>(x >> 8);
 86     uint8_t b7 = static_cast<uint8_t>(x);
 87     Write(b0);
 88     Write(b1);
 89     Write(b2);
 90     Write(b3);
 91     Write(b4);
 92     Write(b5);
 93     Write(b6);
 94     Write(b7);
 95 }
 96 
 97 void BinaryWriter::Write(int64_t x)
 98 {
 99     Write(static_cast<uint64_t>(x));
100 }
101 
102 void BinaryWriter::Write(float x)
103 {
104     uint32_t* u = reinterpret_cast<uint32_t*>(&x);
105     Write(*u);
106 }
107 
108 void BinaryWriter::Write(double x)
109 {
110     uint64_t* u = reinterpret_cast<uint64_t*>(&x);
111     Write(*u);
112 }
113 
114 void BinaryWriter::Write(char x)
115 {
116     Write(static_cast<uint8_t>(x));
117 }
118 
119 void BinaryWriter::Write(char16_t x)
120 {
121     Write(static_cast<uint16_t>(x));
122 }
123 
124 void BinaryWriter::Write(char32_t x)
125 {
126     Write(static_cast<uint32_t>(x));
127 }
128 
129 void BinaryWriter::Write(const std::string& s)
130 {
131     Write(strue);
132 }
133 
134 void BinaryWriter::Write(const std::string& sbool writeNull)
135 {
136     for (char c : s)
137     {
138         uint8_t x = static_cast<uint8_t>(c);
139         Write(x);
140     }
141     if (writeNull)
142     {
143         Write(static_cast<uint8_t>(0));
144     }
145 }
146 
147 void BinaryWriter::Write(const std::u16string& s)
148 {
149     std::string utf8_str = ToUtf8(s);
150     Write(utf8_str);
151 }
152 
153 void BinaryWriter::Write(const std::u32string& s)
154 {
155     std::string utf8_str = ToUtf8(s);
156     Write(utf8_str);
157 }
158 
159 void BinaryWriter::WriteULEB128UInt(uint32_t x)
160 {
161     do
162     {
163         uint8_t b = x & 0x7F;
164         x >>= 7;
165         if (x != 0)
166         {
167             b |= 0x80;
168         }
169         Write(b);
170     }
171     while (x != 0);
172 }
173 
174 void BinaryWriter::WriteULEB128ULong(uint64_t x)
175 {
176     do
177     {
178         uint8_t b = x & 0x7F;
179         x >>= 7;
180         if (x != 0)
181         {
182             b |= 0x80;
183         }
184         Write(b);
185     }
186     while (x != 0);
187 }
188 
189 void BinaryWriter::WriteSLEB128Int(int32_t x)
190 {
191     bool more = true;
192     bool negative = x < 0;
193     while (more)
194     {
195         uint8_t b = x & 0x7F;
196         x >>= 7;
197         if (negative)
198         {
199             x |= ~int32_t(0) << (32 - 7);
200         }
201         if (x == 0 && (b & 0x40) == 0 || x == -1 && (b & 0x40) != 0)
202         {
203             more = false;
204         }
205         else
206         {
207             b |= 0x80;
208         }
209         Write(b);
210     }
211 }
212 
213 void BinaryWriter::WriteSLEB128Long(int64_t x)
214 {
215     bool more = true;
216     bool negative = x < 0;
217     while (more)
218     {
219         uint8_t b = x & 0x7F;
220         x >>= 7;
221         if (negative)
222         {
223             x |= ~int64_t(0) << (64 - 7);
224         }
225         if (x == 0 && (b & 0x40) == 0 || x == -1 && (b & 0x40) != 0)
226         {
227             more = false;
228         }
229         else
230         {
231             b |= 0x80;
232         }
233         Write(b);
234     }
235 }
236 
237 void BinaryWriter::Write(const boost::uuids::uuid& uuid)
238 {
239     for (boost::uuids::uuid::value_type x : uuid)
240     {
241         Write(x);
242     }
243 }
244 
245 void BinaryWriter::Seek(uint32_t pos_)
246 {
247     FlushBuffer();
248     pos = pos_;
249     int result = fseek(fileposSEEK_SET);
250     if (result != 0)
251     {
252         throw std::runtime_error("seek failed: " + soulng::util::PlatformStringToUtf8(std::strerror(errno)));
253     }
254 }
255 
256 void BinaryWriter::FlushBuffer()
257 {
258     if (bufp != buffer)
259     {
260         size_t n = bufp - buffer;
261         size_t numWritten = fwrite(buffer1nfile);
262         if (numWritten != n)
263         {
264             throw std::runtime_error("could not write to '" + fileName + "': " + soulng::util::PlatformStringToUtf8(std::strerror(errno)));
265         }
266         BufferReset();
267     }
268 }
269 
270 } } // namespace soulng::util
271