Lua: Don't lua_error() out of context with pending dtors
[lsnes.git] / src / library / binarystream.cpp
blobedfa871cce29004a2d82760495e594fb34763825
1 #include "binarystream.hpp"
2 #include "serialization.hpp"
3 #include "minmax.hpp"
4 #include "string.hpp"
5 #include <algorithm>
6 #include <iostream>
7 #include <iterator>
8 #include <map>
9 #include <unistd.h>
11 //Damn Windows.
12 #ifndef EWOULDBLOCK
13 #define EWOULDBLOCK EAGAIN
14 #endif
16 namespace
18 void write_whole(int s, const char* buf, size_t size)
20 size_t w = 0;
21 while(w < size) {
22 int maxw = 32767;
23 if((size_t)maxw > (size - w))
24 maxw = size - w;
25 int r = write(s, buf + w, maxw);
26 if(r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
27 continue;
28 if(r < 0) {
29 int err = errno;
30 (stringfmt() << strerror(err)).throwex();
32 w += r;
36 size_t whole_read(int s, char* buf, size_t size)
38 size_t r = 0;
39 while(r < size) {
40 int maxr = 32767;
41 if((size_t)maxr > (size - r))
42 maxr = size - r;
43 int x = read(s, buf + r, maxr);
44 if(x < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
45 continue;
46 if(x < 0) {
47 int err = errno;
48 (stringfmt() << strerror(err)).throwex();
50 if(x == 0)
51 break; //EOF.
52 r += x;
54 return r;
58 namespace binarystream
60 const uint32_t TAG_ = 0xaddb2d86;
62 output::output()
63 : strm(-1)
67 output::output(int s)
68 : strm(s)
72 void output::byte(uint8_t byte)
74 write(reinterpret_cast<char*>(&byte), 1);
77 void output::number(uint64_t number)
79 char data[10];
80 size_t len = 0;
81 do {
82 bool cont = (number > 127);
83 data[len++] = (cont ? 0x80 : 0x00) | (number & 0x7F);
84 number >>= 7;
85 } while(number);
86 write(data, len);
89 size_t output::numberbytes(uint64_t number)
91 size_t o = 0;
92 do {
93 o++;
94 number >>= 7;
95 } while(number);
96 return o;
99 size_t output::stringbytes(const std::string& string)
101 size_t slen = string.length();
102 return numberbytes(slen) + slen;
105 void output::number32(uint32_t number)
107 char data[4];
108 serialization::u32b(data, number);
109 write(data, 4);
112 void output::string(const std::string& string)
114 number(string.length());
115 std::vector<char> tmp(string.begin(), string.end());
116 write(&tmp[0], tmp.size());
119 void output::string_implicit(const std::string& string)
121 std::vector<char> tmp(string.begin(), string.end());
122 write(&tmp[0], tmp.size());
125 void output::blob_implicit(const std::vector<char>& blob)
127 write(&blob[0], blob.size());
130 void output::raw(const void* buf, size_t bufsize)
132 write(reinterpret_cast<const char*>(buf), bufsize);
135 void output::write_extension_tag(uint32_t tag, uint64_t size)
137 number32(TAG_);
138 number32(tag);
139 number(size);
142 void output::extension(uint32_t tag, std::function<void(output&)> fn, bool even_empty)
144 output tmp;
145 fn(tmp);
146 if(!even_empty && !tmp.buf.size())
147 return;
148 number32(TAG_);
149 number32(tag);
150 number(tmp.buf.size());
151 blob_implicit(tmp.buf);
154 void output::extension(uint32_t tag, std::function<void(output&)> fn, bool even_empty,
155 size_t size_precognition)
157 if(!even_empty && !size_precognition)
158 return;
159 number32(TAG_);
160 number32(tag);
161 number(size_precognition);
162 fn(*this);
165 void output::write(const char* ibuf, size_t size)
167 if(strm >= 0)
168 write_whole(strm, ibuf, size);
169 else {
170 size_t o = buf.size();
171 buf.resize(o + size);
172 memcpy(&buf[o], ibuf, size);
176 std::string output::get()
178 if(strm >= 0)
179 throw std::logic_error("Get can only be used without explicit sink");
180 return std::string(buf.begin(), buf.end());
183 uint8_t input::byte()
185 char byte;
186 read(&byte, 1);
187 return byte;
190 uint64_t input::number()
192 uint64_t s = 0;
193 int sh = 0;
194 uint8_t c;
195 do {
196 read(reinterpret_cast<char*>(&c), 1);
197 s |= (static_cast<uint64_t>(c & 0x7F) << sh);
198 sh += 7;
199 } while(c & 0x80);
200 return s;
203 uint32_t input::number32()
205 char c[4];
206 read(c, 4);
207 return serialization::u32b(c);
210 std::string input::string()
212 size_t sz = number();
213 std::vector<char> _r;
214 _r.resize(sz);
215 read(&_r[0], _r.size());
216 std::string r(_r.begin(), _r.end());
217 return r;
220 std::string input::string_implicit()
222 if(!parent)
223 throw std::logic_error("binarystream::input::string_implicit() can only be used in substreams");
224 std::vector<char> _r;
225 _r.resize(left);
226 read(&_r[0], left);
227 std::string r(_r.begin(), _r.end());
228 return r;
231 void input::blob_implicit(std::vector<char>& blob)
233 if(!parent)
234 throw std::logic_error("binarystream::input::string_implicit() can only be used in substreams");
235 blob.resize(left);
236 read(&blob[0], left);
239 input::input(int s)
240 : parent(NULL), strm(s), left(0)
244 input::input(input& s, uint64_t len)
245 : parent(&s), strm(s.strm), left(len)
247 if(parent->parent && left > parent->left)
248 throw std::runtime_error("Substream length greater than its parent");
251 void input::raw(void* buf, size_t bufsize)
253 read(reinterpret_cast<char*>(buf), bufsize);
256 void input::extension(std::function<void(uint32_t tag, input& s)> fn)
258 extension({}, fn);
261 void input::extension(std::initializer_list<binary_tag_handler> funcs,
262 std::function<void(uint32_t tag, input& s)> default_hdlr)
264 std::map<uint32_t, std::function<void(input& s)>> fn;
265 for(auto i : funcs)
266 fn[i.tag] = i.fn;
267 while(!parent || left > 0) {
268 char c[4];
269 if(!read(c, 4, true))
270 break;
271 uint32_t tagid = serialization::u32b(c);
272 if(tagid != TAG_)
273 throw std::runtime_error("Binary file packet structure desync");
274 uint32_t tag = number32();
275 uint64_t size = number();
276 input ss(*this, size);
277 if(fn.count(tag))
278 fn[tag](ss);
279 else
280 default_hdlr(tag, ss);
281 ss.flush();
285 void input::flush()
287 if(!parent)
288 throw std::logic_error("binarystream::input::flush() can only be used in substreams");
289 char buf[256];
290 while(left)
291 read(buf, min(left, (uint64_t)256));
294 bool input::read(char* buf, size_t size, bool allow_none)
296 if(parent) {
297 if(left == 0 && allow_none)
298 return false;
299 if(size > left)
300 std::runtime_error("Substream unexpected EOF");
301 parent->read(buf, size, false);
302 left -= size;
303 } else {
304 size_t r = whole_read(strm, buf, size);
305 if(r < size) {
306 if(!r && allow_none)
307 return false;
308 throw std::runtime_error("Unexpected EOF");
311 return true;
314 void null_default(uint32_t tag, input& s)
316 //no-op