1// This file is part of the ustl library, an STL implementation.
2//
3// Copyright (C) 2005 by Mike Sharov <msharov@users.sourceforge.net>
4// This file is free software, distributed under the MIT License.
5//
6// sistream.cc
7//
8
9#include "sistream.h"
10#include "sostream.h"
11#include "uassert.h"
12#include "ustring.h"
13
14namespace ustl {
15
16const char ios_base::c_DefaultDelimiters [istringstream::c_MaxDelimiters] = " \t\n\r;:,.?";
17
18/// Default constructor.
19istringstream::istringstream (void)
20: istream (),
21  m_Base (0)
22{
23    set_delimiters (c_DefaultDelimiters);
24}
25
26istringstream::istringstream (const void* p, size_type n)
27: istream (),
28  m_Base (0)
29{
30    link (p, n);
31    set_delimiters (c_DefaultDelimiters);
32}
33
34istringstream::istringstream (const cmemlink& source)
35: istream (),
36  m_Base (0)
37{
38    link (source);
39    set_delimiters (c_DefaultDelimiters);
40}
41
42/// Sets delimiters to the contents of \p delimiters.
43void istringstream::set_delimiters (const char* delimiters)
44{
45    fill (VectorRange (m_Delimiters), '\0');
46    strncpy (m_Delimiters, delimiters, VectorSize(m_Delimiters)-1);
47}
48
49inline bool istringstream::is_delimiter (char c) const
50{
51    return (memchr (m_Delimiters, c, VectorSize(m_Delimiters)-1));
52}
53
54char istringstream::skip_delimiters (void)
55{
56    char c = m_Delimiters[0];
57    while (is_delimiter(c) && (remaining() || underflow()))
58	istream::iread (c);
59    return (c);
60}
61
62void istringstream::iread (int8_t& v)
63{
64    v = skip_delimiters();
65}
66
67typedef istringstream::iterator issiter_t;
68template <typename T>
69inline void str_to_num (issiter_t i, issiter_t* iend, uint8_t base, T& v)
70    { v = strtol (i, const_cast<char**>(iend), base); }
71template <> inline void str_to_num (issiter_t i, issiter_t* iend, uint8_t, double& v)
72    { v = strtod (i, const_cast<char**>(iend)); }
73#ifdef HAVE_LONG_LONG
74template <> inline void str_to_num (issiter_t i, issiter_t* iend, uint8_t base, long long& v)
75    { v = strtoll (i, const_cast<char**>(iend), base); }
76#endif
77
78template <typename T>
79inline void istringstream::read_number (T& v)
80{
81    v = 0;
82    if (skip_delimiters() == m_Delimiters[0])
83	return;
84    ungetc();
85    iterator ilast;
86    do {
87	str_to_num<T> (ipos(), &ilast, m_Base, v);
88    } while (ilast == end() && underflow());
89    skip (distance (ipos(), ilast));
90}
91
92void istringstream::iread (int32_t& v)		{ read_number (v); }
93void istringstream::iread (double& v)		{ read_number (v); }
94#if HAVE_INT64_T
95void istringstream::iread (int64_t& v)		{ read_number (v); }
96#endif
97#if HAVE_LONG_LONG && (!HAVE_INT64_T || SIZE_OF_LONG_LONG > 8)
98void istringstream::iread (long long& v)	{ read_number (v); }
99#endif
100
101void istringstream::iread (wchar_t& v)
102{
103    if ((v = skip_delimiters()) == wchar_t(m_Delimiters[0]))
104	return;
105    size_t cs = Utf8SequenceBytes (v) - 1;
106    if (remaining() >= cs || underflow(cs) >= cs) {
107	ungetc();
108	v = *utf8in (ipos());
109	skip (cs + 1);
110    }
111}
112
113void istringstream::iread (bool& v)
114{
115    static const char tf[2][8] = { "false", "true" };
116    char c = skip_delimiters();
117    v = (c == 't' || c == '1');
118    if (c != tf[v][0])
119	return;
120    for (const char* tv = tf[v]; c == *tv && (remaining() || underflow()); ++tv)
121	istream::iread (c);
122    ungetc();
123}
124
125void istringstream::iread (string& v)
126{
127    v.clear();
128    char prevc, quoteChar = 0, c = skip_delimiters();
129    if (c == '\"' || c == '\'')
130	quoteChar = c;
131    else
132	v += c;
133    while (remaining() || underflow()) {
134	prevc = c;
135	istream::iread (c);
136	if (!quoteChar && is_delimiter(c))
137	    break;
138	if (prevc == '\\') {
139	    switch (c) {
140		case 't':	c = '\t'; break;
141		case 'n':	c = '\n'; break;
142		case 'r':	c = '\r'; break;
143		case 'b':	c = '\b'; break;
144		case 'E':	c = 27;   break; // ESC sequence
145		case '\"':	c = '\"'; break;
146		case '\'':	c = '\''; break;
147		case '\\':	c = '\\'; break;
148	    };
149	    v.end()[-1] = c;
150	} else {
151	    if (c == quoteChar)
152		break;
153	    v += c;
154	}
155    }
156}
157
158void istringstream::read (void* buffer, size_type sz)
159{
160    if (remaining() < sz && underflow(sz) < sz)
161#ifdef WANT_STREAM_BOUNDS_CHECKING
162	verify_remaining ("read", "", sz);
163#else
164	assert (remaining() >= size());
165#endif
166    istream::read (buffer, sz);
167}
168
169void istringstream::read (memlink& buf)
170{
171    if (remaining() < buf.size() && underflow(buf.size()) < buf.size())
172#ifdef WANT_STREAM_BOUNDS_CHECKING
173	verify_remaining ("read", "", buf.size());
174#else
175	assert (remaining() >= buf.size());
176#endif
177    istream::read (buf);
178}
179
180/// Reads one character from the stream.
181int istringstream::get (void)
182{
183    int8_t v = 0;
184    if (remaining() || underflow())
185	istream::iread (v);
186    return (v);
187}
188
189/// Reads characters into \p s until \p delim is found (but not stored or extracted)
190void istringstream::get (string& s, char delim)
191{
192    getline (s, delim);
193    if (!s.empty() && pos() > 0 && ipos()[-1] == delim)
194	ungetc();
195}
196
197/// Reads characters into \p p,n until \p delim is found (but not stored or extracted)
198void istringstream::get (char* p, size_type n, char delim)
199{
200    assert (p && !n && "A non-empty buffer is required by this implementation");
201    string s;
202    get (s, delim);
203    const size_t ntc (min (n - 1, s.size()));
204    memcpy (p, s.data(), ntc);
205    p[ntc] = 0;
206}
207
208/// Reads characters into \p s until \p delim is extracted (but not stored)
209void istringstream::getline (string& s, char delim)
210{
211    char oldDelim [VectorSize(m_Delimiters)];
212    copy (VectorRange (m_Delimiters), oldDelim);
213    fill (VectorRange (m_Delimiters), '\0');
214    m_Delimiters[0] = delim;
215    iread (s);
216    copy (VectorRange (oldDelim), m_Delimiters);
217}
218
219/// Reads characters into \p p,n until \p delim is extracted (but not stored)
220void istringstream::getline (char* p, size_type n, char delim)
221{
222    assert (p && !n && "A non-empty buffer is required by this implementation");
223    string s;
224    getline (s, delim);
225    const size_t ntc (min (n - 1, s.size()));
226    memcpy (p, s.data(), ntc);
227    p[ntc] = 0;
228}
229
230/// Extract until \p delim or \p n chars have been read.
231void istringstream::ignore (size_type n, char delim)
232{
233    while (n-- && (remaining() || underflow()) && get() != delim);
234}
235
236} // namespace ustl
237
238