Cleanup lua code by introducing lua::functions
[lsnes.git] / src / lua / iconv.cpp
blobd5afabcb459d41f74666be435123275472f83da6
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 ~lua_iconv() throw();
62 int call(lua::state& L, lua::parameters& P);
63 static int create(lua::state& L, lua::parameters& P);
64 std::string print()
66 return spec;
68 private:
69 iconv_t ctx;
70 buffer input();
71 std::string spec;
74 lua::_class<lua_iconv> class_iconv(lua_class_pure, "ICONV", {
75 {"new", lua_iconv::create},
76 }, {
77 {"__call", &lua_iconv::call},
78 }, &lua_iconv::print);
81 lua_iconv::lua_iconv(lua::state& L, const char* from, const char* to)
83 spec = std::string(from) + "->" + to;
84 errno = 0;
85 ctx = iconv_open(to, from);
86 if(errno) {
87 int err = errno;
88 (stringfmt() << "Error creating character set converter: " << strerror(err)).throwex();
92 lua_iconv::~lua_iconv() throw()
94 iconv_close(ctx);
97 int lua_iconv::call(lua::state& L, lua::parameters& P)
99 std::string src;
101 P(P.skipped(), src);
103 std::string dst;
104 buffer input(false, src);
105 buffer output(true, dst);
106 std::string code = "";
107 while(true) {
108 auto _input = input.get();
109 auto _output = output.get();
110 int r = iconv(ctx, &_input.first, &_input.second, &_output.first, &_output.second);
111 size_t unprocessed = _input.second;
112 input.set(_input);
113 output.set(_output);
114 if(r < 0) {
115 int err = errno;
116 switch(err) {
117 case E2BIG:
118 continue; //Just retry with new output bufer.
119 case EILSEQ:
120 code = "INVALID";
121 goto exit;
122 case EINVAL:
123 if(unprocessed != input.unprocessed())
124 continue; //Retry.
125 code = "INCOMPLETE";
126 goto exit;
127 default:
128 code = "INTERNALERR";
129 goto exit;
131 } else if(!input.unprocessed())
132 break;
134 exit:
135 L.pushboolean(!code.length());
136 L.pushlstring(dst);
137 if(code.length()) {
138 L.pushnumber(input.left());
139 L.pushlstring(code);
141 return code.length() ? 4 : 2;
144 int lua_iconv::create(lua::state& L, lua::parameters& P)
146 std::string from, to;
148 P(from, to);
150 lua::_class<lua_iconv>::create(L, from.c_str(), to.c_str());
151 return 1;
154 int _lsnes_string_byteU(lua::state& L, lua::parameters& P)
156 std::string _str;
157 size_t i, j;
159 P(_str, P.optional(i, 1), P.optional2(j, i));
161 std::u32string str = utf8::to32(_str);
162 if(i == 0) i = 1;
163 size_t p = 0;
164 for(size_t k = i - 1; k < j && k < str.length(); k++) {
165 L.pushnumber(str[k]);
166 p++;
168 return p;
171 int _lsnes_string_charU(lua::state& L, lua::parameters& P)
173 std::u32string str;
174 while(P.more()) {
175 uint32_t cp;
177 P(cp);
179 //Surrogates are not valid unicode.
180 if((cp & 0xD800) == 0xD800)
181 throw std::runtime_error("Invalid character");
182 //Explicit noncharacters.
183 if(cp >= 0xFDD0 && cp < 0xFDF0)
184 throw std::runtime_error("Invalid character");
185 //The last two characters of each plane are noncharacters.
186 if((cp & 0xFFFE) == 0xFFFE)
187 throw std::runtime_error("Invalid character");
188 //Last valid plane is plane 16.
189 if((cp >> 16) > 16)
190 throw std::runtime_error("Invalid character");
191 //Ok.
192 str += std::u32string(1, cp);
194 L.pushlstring(utf8::to8(str));
195 return 1;
198 lua::functions iconv_fns(lua_func_bit, "", {
199 {"_lsnes_string_byteU", _lsnes_string_byteU},
200 {"_lsnes_string_charU", _lsnes_string_charU},