1// vi:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>
4 *     economic rights: Technische Universität Dresden (Germany)
5 *
6 * This file is part of TUD:OS and distributed under the terms of the
7 * GNU General Public License 2.
8 * Please see the COPYING-GPL-2 file for details.
9 *
10 * As a special exception, you may use this file as part of a free software
11 * library without restriction.  Specifically, if other files instantiate
12 * templates or use macros or inline functions from this file, or you compile
13 * this file and link it with other files to produce an executable, this
14 * file does not by itself cause the resulting executable to be covered by
15 * the GNU General Public License.  This exception does not however
16 * invalidate any other reasons why the executable file might be covered by
17 * the GNU General Public License.
18 */
19
20/**
21 * Strings.
22 */
23#pragma once
24
25#include <l4/cxx/minmax>
26#include <l4/cxx/basic_ostream>
27
28
29namespace cxx {
30
31/**
32 * Allocation free string class with explicit length field.
33 *
34 * This class is used to group characters of a string which belong
35 * to one syntactical token types number, identifier, string,
36 * whitespace or another single character.
37 *
38 * Stings in this class can contain null bytes and may denote parts of
39 * other strings.
40 */
41class String
42{
43public:
44
45  /// Character index type.
46  typedef char const *Index;
47
48  /// Initialize from a zero-terminated string.
49  String(char const *s) throw() : _start(s), _len(__builtin_strlen(s)) {}
50  /// Initialize from a pointer to first character and a length.
51  String(char const *s, unsigned long len) throw() : _start(s), _len(len) {}
52
53  /**
54   * Initialize with start and end pointer.
55   *
56   * \param s  first character of the string
57   * \param e  pointer to first byte behind the string
58   */
59  String(char const *s, char const *e) throw() : _start(s), _len(e - s) {}
60
61  /// Zero-initialize. Create an invalid string.
62  String() : _start(0), _len(0) {}
63
64  /// Pointer to first character.
65  Index start() const { return _start; }
66  /// Pointer to first byte behind the string.
67  Index end() const { return _start + _len; }
68  /// Length.
69  int len() const { return _len; }
70
71  /// Set start.
72  void start(char const *s) { _start = s; }
73  /// Set length.
74  void len(unsigned long len) { _len = len; }
75  /// Check if the string has length zero.
76  bool empty() const { return !_len; }
77
78  /// Return prefix up to index.
79  String head(Index end) const
80  {
81    if (end < _start)
82      return String();
83
84    if (eof(end))
85      return *this;
86
87    return String(_start, end - _start);
88  }
89
90  /// Prefix of length `end`.
91  String head(unsigned long end) const
92  { return head(start() + end); }
93
94  /// Substring of length `len` starting at `idx`.
95  String substr(unsigned long idx, unsigned long len = ~0UL) const
96  {
97    if (idx >= _len)
98      return String(end(), 0UL);
99
100    return String(_start + idx, cxx::min(len, _len - idx));
101  }
102
103  /// Substring of length `len` starting at `start`.
104  String substr(char const *start, unsigned long len = 0) const
105  {
106    if (start >= _start && !eof(start))
107      {
108	unsigned long nlen = _start + _len - start;
109	if (len != 0)
110	  nlen = cxx::min(nlen, len);
111	return String(start, nlen);
112      }
113
114    return String(end(), 0UL);
115  }
116
117  /// Find matching character. `match` should be a function such as `isspace`.
118  template< typename F >
119  char const *find_match(F &&match) const
120  {
121    String::Index s = _start;
122    while (1)
123      {
124	if (eof(s))
125	  return s;
126
127	if (match(*s))
128	  return s;
129
130	++s;
131      }
132  }
133
134  /// Find character. Return end() if not found.
135  char const *find(char const *c) const
136  { return find(c, start());  }
137
138  /// Find character. Return end() if not found.
139  char const *find(int c) const
140  { return find(c, start());  }
141
142  /// Find right-most character. Return end() if not found.
143  char const *rfind(char const *c) const
144  {
145    if (!_len)
146      return end();
147
148    char const *p = end();
149    --p;
150    while (p >= _start)
151      {
152	if (*p == *c)
153	  return p;
154	--p;
155      }
156    return end();
157
158  }
159
160  /**
161   * Check if `c` is a prefix of string.
162   *
163   * \return 0 if `c` is not a prefix, if it is a prefix, return
164   *         first position not in `c` (which might be end()).
165   */
166  Index starts_with(cxx::String const &c) const
167  {
168    unsigned long i;
169    for (i = 0; i < c._len && i < _len; ++i)
170      if (_start[i] != c[i])
171	return 0;
172    return i == c._len ? start() + i : 0;
173  }
174
175  /// Find character `c` starting at position `s`. Return end() if not found.
176  char const *find(int c, char const *s) const
177  {
178    if (s < _start)
179      return end();
180
181    while (1)
182      {
183	if (eof(s))
184	  return s;
185
186	if (*s == c)
187	  return s;
188
189	++s;
190      }
191  }
192
193  /**
194   * Find character set at position.
195   *
196   * \param c  zero-terminated string of characters to search for
197   * \param s  start position of search in string
198   *
199   * \retval end()     if no char in `c` is contained in string at or behind `s`.
200   * \retval position  in string of some character in `c`.
201   */
202  char const *find(char const *c, char const *s) const
203  {
204    if (s < _start)
205      return end();
206
207    while (1)
208      {
209	if (eof(s))
210	  return s;
211
212	for (char const *x = c; *x; ++x)
213	  if (*s == *x)
214	    return s;
215
216	++s;
217      }
218  }
219
220  /// Get character at `idx`.
221  char const &operator [] (unsigned long idx) const { return _start[idx]; }
222  /// Get character at `idx`.
223  char const &operator [] (int idx) const { return _start[idx]; }
224  /// Get character at `idx`.
225  char const &operator [] (Index idx) const { return *idx; }
226
227  /// Check if pointer `s` points behind string.
228  bool eof(char const *s) const { return s >= _start + _len || !*s; }
229
230  /**
231   * Convert decimal string to integer.
232   *
233   * \tparam     INT  result integer type
234   * \param[out] v    conversion result
235   *
236   * \return position of first character not converted.
237   */
238  template<typename INT>
239  int from_dec(INT *v) const
240  {
241    *v = 0;
242    Index c;
243    for (c = start(); !eof(c); ++c)
244      {
245	unsigned char n;
246	if (*c >= '0' && *c <= '9')
247	  n = *c - '0';
248	else
249	  return c - start();
250
251        *v *= 10;
252	*v += n;
253      }
254    return c - start();
255  }
256
257  /**
258   * Convert hex string to integer.
259   *
260   * \tparam     INT  result integer type
261   * \param[out] v    conversion result
262   *
263   * \retval -1        if the maximal amount of digits fitting into `INT` have
264   *                   been read,
265   * \retval position  of first character not converted otherwise.
266   */
267  template<typename INT>
268  int from_hex(INT *v) const
269  {
270    *v = 0;
271    unsigned shift = 0;
272    Index c;
273    for (c = start(); !eof(c); ++c)
274      {
275	shift += 4;
276	if (shift > sizeof(INT) * 8)
277	  return -1;
278	unsigned char n;
279	if (*c >= '0' && *c <= '9')
280	  n = *c - '0';
281	else if (*c >= 'A' && *c <= 'F')
282	  n = *c - 'A' + 10;
283	else if (*c >= 'a' && *c <= 'f')
284	  n = *c - 'a' + 10;
285	else
286	  return c - start();
287
288        *v <<= 4;
289	*v |= n;
290      }
291    return c - start();
292  }
293
294  /// Equality.
295  bool operator == (String const &o) const
296  {
297    if (len() != o.len())
298      return false;
299
300    for (unsigned long i = 0; i < _len; ++i)
301      if (_start[i] != o._start[i])
302	return false;
303
304    return true;
305  }
306
307  /// Inequality.
308  bool operator != (String const &o) const
309  { return ! (operator == (o)); }
310
311private:
312  char const *_start;
313  unsigned long _len;
314};
315
316}
317
318/// Write `str` on `s`.
319inline
320L4::BasicOStream &operator << (L4::BasicOStream &s, cxx::String const &str)
321{
322  s.write(str.start(), str.len());
323  return s;
324}
325