-
Notifications
You must be signed in to change notification settings - Fork 1
/
iconv.cc
152 lines (125 loc) · 3.92 KB
/
iconv.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* This file is (c) 2008-2012 Konstantin Isakov <ikm@goldendict.org>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "iconv.hh"
#include <vector>
#include <errno.h>
#include <string.h>
#ifdef __WIN32
char const * const Iconv::GdWchar = "UCS-4LE";
#else
char const * const Iconv::GdWchar = "WCHAR_T";
#endif
char const * const Iconv::Utf16Le = "UTF-16LE";
char const * const Iconv::Utf8 = "UTF-8";
using gd::wchar;
Iconv::Iconv( char const * to, char const * from ) throw( exCantInit ):
state( iconv_open( to, from ) )
{
if ( state == (iconv_t) -1 )
throw exCantInit( strerror( errno ) );
}
void Iconv::reinit( char const * to, char const * from ) throw( exCantInit )
{
iconv_close( state );
state = iconv_open( to, from );
if ( state == (iconv_t) -1 )
throw exCantInit( strerror( errno ) );
}
Iconv::~Iconv() throw()
{
iconv_close( state );
}
Iconv::Result Iconv::convert( void const * & inBuf, size_t & inBytesLeft,
void * & outBuf, size_t & outBytesLeft )
throw( exIncorrectSeq, exOther )
{
size_t result = iconv( state,
// #ifdef __WIN32
// (char const **)&inBuf,
// #else
(char **)&inBuf,
// #endif
&inBytesLeft,
(char **)&outBuf, &outBytesLeft );
if ( result == (size_t) -1 )
{
switch( errno )
{
case EILSEQ:
throw exIncorrectSeq();
case EINVAL:
return NeedMoreIn;
case E2BIG:
return NeedMoreOut;
default:
throw exOther( strerror( errno ) );
}
}
return Success;
}
gd::wstring Iconv::toWstring( char const * fromEncoding, void const * fromData,
size_t dataSize )
throw( exCantInit, exIncorrectSeq, exPrematureEnd, exOther )
{
/// Special-case the dataSize == 0 to avoid any kind of iconv-specific
/// behaviour in that regard.
if ( !dataSize )
return gd::wstring();
Iconv ic( GdWchar, fromEncoding );
/// This size is usually enough, but may be enlarged during the conversion
std::vector< wchar > outBuf( dataSize );
void * outBufPtr = &outBuf.front();
size_t outBufLeft = outBuf.size() * sizeof( wchar );
for( ; ; )
{
switch( ic.convert( fromData, dataSize, outBufPtr, outBufLeft ) )
{
case Success:
return gd::wstring( &outBuf.front(),
outBuf.size() - outBufLeft / sizeof( wchar ) );
case NeedMoreIn:
throw exPrematureEnd();
case NeedMoreOut:
{
// Grow the buffer and retry
// The pointer may get invalidated so we save the diff and restore it
size_t offset = (wchar *)outBufPtr - &outBuf.front();
outBuf.resize( outBuf.size() + 256 );
outBufPtr = &outBuf.front() + offset;
outBufLeft += 256;
}
}
}
}
std::string Iconv::toUtf8( char const * fromEncoding, void const * fromData,
size_t dataSize )
throw( exCantInit, exIncorrectSeq, exPrematureEnd, exOther )
{
// Similar to toWstring
if ( !dataSize )
return std::string();
Iconv ic( Utf8, fromEncoding );
std::vector< char > outBuf( dataSize );
void * outBufPtr = &outBuf.front();
size_t outBufLeft = outBuf.size();
for( ; ; )
{
switch( ic.convert( fromData, dataSize, outBufPtr, outBufLeft ) )
{
case Success:
return std::string( &outBuf.front(),
outBuf.size() - outBufLeft );
case NeedMoreIn:
throw exPrematureEnd();
case NeedMoreOut:
{
// Grow the buffer and retry
// The pointer may get invalidated so we save the diff and restore it
size_t offset = (char *)outBufPtr - &outBuf.front();
outBuf.resize( outBuf.size() + 256 );
outBufPtr = &outBuf.front() + offset;
outBufLeft += 256;
}
}
}
}