Add base support for Lua object overcommit
[lsnes.git] / src / lua / iconv.cpp
blobae10dadcb5423249e0eafe3d7bb4cea2a2cbafe9
1 #include <iconv.h>
2 #include "lua/internal.hpp"
3 #include "library/string.hpp"
4 #include "library/utf8.hpp"
5 #include <cstring>
6 #include <cerrno>
8 namespace
10 struct buffer
12 buffer(bool _is_out, std::string& _str)
13 : str(_str), is_out(_is_out)
15 buffer_size = 0;
16 char_count = 0;
17 if(!is_out) {
18 while(buffer_size < sizeof(buf) && char_count < str.length())
19 buf[buffer_size++] = str[char_count++];
22 std::pair<char*,size_t> get()
24 return std::make_pair(buf, is_out ? sizeof(buf) : buffer_size);
26 void set(std::pair<char*,size_t> pos)
28 if(is_out) {
29 size_t emitted = sizeof(buf) - pos.second;
30 str.resize(str.length() + emitted);
31 std::copy(pos.first - emitted, pos.first, str.begin() + char_count);
32 char_count += emitted;
33 } else {
34 size_t eaten = buffer_size - pos.second;
35 memmove(buf, buf + eaten, buffer_size - eaten);
36 buffer_size -= eaten;
37 while(buffer_size < sizeof(buf) && char_count < str.length())
38 buf[buffer_size++] = str[char_count++];
41 size_t left()
43 return buffer_size + str.length() - char_count;
45 size_t unprocessed()
47 return buffer_size;
49 private:
50 char buf[1024];
51 size_t buffer_size;
52 size_t char_count;
53 std::string& str;
54 bool is_out;
57 struct lua_iconv
59 public:
60 lua_iconv(lua::state& L, const char* from, const char* to);
61 static size_t overcommit(const char* from, const char* to) { return 0; }
62 ~lua_iconv() throw();
63 int call(lua::state& L, lua::parameters& P);
64 static int create(lua::state& L, lua::parameters& P);
65 std::string print()
67 return spec;
69 private:
70 iconv_t ctx;
71 buffer input();
72 std::string spec;
75 lua::_class<lua_iconv> class_iconv(lua_class_pure, "ICONV", {
76 {"new", lua_iconv::create},
77 }, {
78 {"__call", &lua_iconv::call},
79 }, &lua_iconv::print);
82 lua_iconv::lua_iconv(lua::state& L, const char* from, const char* to)
84 spec = std::string(from) + "->" + to;
85 errno = 0;
86 ctx = iconv_open(to, from);
87 if(errno) {
88 int err = errno;
89 (stringfmt() << "Error creating character set converter: " << strerror(err)).throwex();
93 lua_iconv::~lua_iconv() throw()
95 iconv_close(ctx);
98 int lua_iconv::call(lua::state& L, lua::parameters& P)
100 std::string src;
102 P(P.skipped(), src);
104 std::string dst;
105 buffer input(false, src);
106 buffer output(true, dst);
107 std::string code = "";
108 while(true) {
109 auto _input = input.get();
110 auto _output = output.get();
111 int r = iconv(ctx, &_input.first, &_input.second, &_output.first, &_output.second);
112 size_t unprocessed = _input.second;
113 input.set(_input);
114 output.set(_output);
115 if(r < 0) {
116 int err = errno;
117 switch(err) {
118 case E2BIG:
119 continue; //Just retry with new output bufer.
120 case EILSEQ:
121 code = "INVALID";
122 goto exit;
123 case EINVAL:
124 if(unprocessed != input.unprocessed())
125 continue; //Retry.
126 code = "INCOMPLETE";
127 goto exit;
128 default:
129 code = "INTERNALERR";
130 goto exit;
132 } else if(!input.unprocessed())
133 break;
135 exit:
136 L.pushboolean(!code.length());
137 L.pushlstring(dst);
138 if(code.length()) {
139 L.pushnumber(input.left());
140 L.pushlstring(code);
142 return code.length() ? 4 : 2;
145 int lua_iconv::create(lua::state& L, lua::parameters& P)
147 std::string from, to;
149 P(from, to);
151 lua::_class<lua_iconv>::create(L, from.c_str(), to.c_str());
152 return 1;
155 int _lsnes_string_byteU(lua::state& L, lua::parameters& P)
157 std::string _str;
158 size_t i, j;
160 P(_str, P.optional(i, 1), P.optional2(j, i));
162 std::u32string str = utf8::to32(_str);
163 if(i == 0) i = 1;
164 size_t p = 0;
165 for(size_t k = i - 1; k < j && k < str.length(); k++) {
166 L.pushnumber(str[k]);
167 p++;
169 return p;
172 int _lsnes_string_charU(lua::state& L, lua::parameters& P)
174 std::u32string str;
175 while(P.more()) {
176 uint32_t cp;
178 P(cp);
180 //Surrogates are not valid unicode.
181 if((cp & 0xD800) == 0xD800)
182 throw std::runtime_error("Invalid character");
183 //Explicit noncharacters.
184 if(cp >= 0xFDD0 && cp < 0xFDF0)
185 throw std::runtime_error("Invalid character");
186 //The last two characters of each plane are noncharacters.
187 if((cp & 0xFFFE) == 0xFFFE)
188 throw std::runtime_error("Invalid character");
189 //Last valid plane is plane 16.
190 if((cp >> 16) > 16)
191 throw std::runtime_error("Invalid character");
192 //Ok.
193 str += std::u32string(1, cp);
195 L.pushlstring(utf8::to8(str));
196 return 1;
199 lua::functions iconv_fns(lua_func_bit, "", {
200 {"_lsnes_string_byteU", _lsnes_string_byteU},
201 {"_lsnes_string_charU", _lsnes_string_charU},