StringRef.h revision c9af366fc39d657b7d416d16988265d86a641184
14cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar//===--- StringRef.h - Constant String Reference Wrapper --------*- C++ -*-===//
24cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar//
34cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar//                     The LLVM Compiler Infrastructure
44cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar//
54cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar// This file is distributed under the University of Illinois Open Source
64cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar// License. See LICENSE.TXT for details.
74cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar//
84cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar//===----------------------------------------------------------------------===//
94cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
104cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar#ifndef LLVM_ADT_STRINGREF_H
114cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar#define LLVM_ADT_STRINGREF_H
124cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
13d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar#include <algorithm>
1485f49835c288b0107cb4020d4e59e491c146973dDaniel Dunbar#include <cassert>
154cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar#include <cstring>
164cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar#include <string>
174cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
184cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbarnamespace llvm {
19f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar
204cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  /// StringRef - Represent a constant reference to a string, i.e. a character
214cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  /// array and a length, which need not be null terminated.
224cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  ///
234cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  /// This class does not own the string data, it is expected to be used in
244cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  /// situations where the character data resides in some other buffer, whose
254cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  /// lifetime extends past that of the StringRef. For this reason, it is not in
264cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  /// general safe to store a StringRef.
274cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  class StringRef {
284cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  public:
294cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    typedef const char *iterator;
30b834a7b73ce0dcf8fbf8d8b0d62f69e4b78059adDaniel Dunbar    static const size_t npos = ~size_t(0);
314cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
324cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  private:
334cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// The start of the string, in an external buffer.
344cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    const char *Data;
354cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
364cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// The length of the string.
37f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    size_t Length;
384cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
394cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  public:
404cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @name Constructors
414cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @{
424cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
434cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// Construct an empty string ref.
444cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /*implicit*/ StringRef() : Data(0), Length(0) {}
454cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
464cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// Construct a string ref from a cstring.
474cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /*implicit*/ StringRef(const char *Str)
484cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      : Data(Str), Length(::strlen(Str)) {}
494cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
504cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// Construct a string ref from a pointer and length.
514cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /*implicit*/ StringRef(const char *_Data, unsigned _Length)
524cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      : Data(_Data), Length(_Length) {}
534cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
544cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// Construct a string ref from an std::string.
554cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /*implicit*/ StringRef(const std::string &Str)
564cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      : Data(Str.c_str()), Length(Str.length()) {}
574cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
584cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @}
594cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @name Iterators
604cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @{
614cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
624cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    iterator begin() const { return Data; }
634cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
644cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    iterator end() const { return Data + Length; }
654cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
664cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @}
674cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @name String Operations
684cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @{
694cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
704cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// data - Get a pointer to the start of the string (which may not be null
714cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// terminated).
724cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    const char *data() const { return Data; }
734cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
744cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// empty - Check if the string is empty.
754cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    bool empty() const { return Length == 0; }
764cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
774cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// size - Get the string size.
787cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    size_t size() const { return Length; }
797cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
807cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    /// equals - Check for string equality, this is more efficient than
817cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    /// compare() in when the relative ordering of inequal strings isn't needed.
827cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    bool equals(const StringRef &RHS) const {
837cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar      return (Length == RHS.Length &&
847e763ebd982e199224a2d2e0cc802d09d2822b34Chris Lattner              memcmp(Data, RHS.Data, RHS.Length) == 0);
857cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    }
864cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
874cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// compare - Compare two strings; the result is -1, 0, or 1 if this string
884cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// is lexicographically less than, equal to, or greater than the \arg RHS.
894cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    int compare(const StringRef &RHS) const {
904cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      // Check the prefix for a mismatch.
914cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      if (int Res = memcmp(Data, RHS.Data, std::min(Length, RHS.Length)))
924cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar        return Res < 0 ? -1 : 1;
934cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
944cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      // Otherwise the prefixes match, so we only need to check the lengths.
954cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      if (Length == RHS.Length)
964cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar        return 0;
974cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      return Length < RHS.Length ? -1 : 1;
984cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    }
994cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
1004cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// str - Get the contents as an std::string.
1014cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    std::string str() const { return std::string(Data, Length); }
1024cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
1034cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @}
1044cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @name Operator Overloads
1054cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @{
1064cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
1074cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    char operator[](size_t Index) const {
1084cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      assert(Index < Length && "Invalid index!");
1094cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      return Data[Index];
1104cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    }
1114cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
1124cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @}
1134cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @name Type Conversions
1144cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @{
1154cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
1164cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    operator std::string() const {
1174cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar      return str();
1184cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    }
1194cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
1204cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar    /// @}
121f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// @name Utility Functions
122f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// @{
123f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar
124d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// substr - Return a reference to the substring from [Start, Start + N).
125f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    ///
126f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// \param Start - The index of the starting character in the substring; if
127d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// the index is npos or greater than the length of the string then the
128d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// empty substring will be returned.
129f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    ///
130f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// \param N - The number of characters to included in the substring. If N
131f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// exceeds the number of characters remaining in the string, the string
132f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// suffix (starting with \arg Start) will be returned.
133f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    StringRef substr(size_t Start, size_t N = npos) const {
134f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar      Start = std::min(Start, Length);
135f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar      return StringRef(Data + Start, std::min(N, Length - Start));
136f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    }
137f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar
138d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// slice - Return a reference to the substring from [Start, End).
139d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    ///
140d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// \param Start - The index of the starting character in the substring; if
141d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// the index is npos or greater than the length of the string then the
142d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// empty substring will be returned.
143d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    ///
144d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// \param End - The index following the last character to include in the
145d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// substring. If this is npos, or less than \arg Start, or exceeds the
146d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// number of characters remaining in the string, the string suffix
147d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// (starting with \arg Start) will be returned.
148d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    StringRef slice(size_t Start, size_t End) const {
149d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar      Start = std::min(Start, Length);
150d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar      End = std::min(std::max(Start, End), Length);
151d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar      return StringRef(Data + Start, End - Start);
152d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    }
153d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar
154d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// split - Split into two substrings around the first occurence of a
155d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// separator character.
156d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    ///
157d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// If \arg Separator is in the string, then the result is a pair (LHS, RHS)
158d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// such that (*this == LHS + Separator + RHS) is true and RHS is
159d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// maximal. If \arg Separator is not in the string, then the result is a
160d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// pair (LHS, RHS) where (*this == LHS) and (RHS == "").
161d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    ///
162d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// \param Separator - The character to split on.
163d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    /// \return - The split substrings.
164d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    std::pair<StringRef, StringRef> split(char Separator) const {
165d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar      iterator it = std::find(begin(), end(), Separator);
166d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar      if (it == end())
167d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar        return std::make_pair(*this, StringRef());
168d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar      return std::make_pair(StringRef(begin(), it - begin()),
169d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar                            StringRef(it + 1, end() - (it + 1)));
170d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar    }
171d61918fc6898a89df8b0a03e068f234ded010cdfDaniel Dunbar
172f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// startswith - Check if this string starts with the given \arg Prefix.
173f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    bool startswith(const StringRef &Prefix) const {
1747cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar      return substr(0, Prefix.Length).equals(Prefix);
175f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    }
176f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar
177c9af366fc39d657b7d416d16988265d86a641184Daniel Dunbar    /// endswith - Check if this string ends with the given \arg Suffix.
178c9af366fc39d657b7d416d16988265d86a641184Daniel Dunbar    bool endswith(const StringRef &Suffix) const {
179c9af366fc39d657b7d416d16988265d86a641184Daniel Dunbar      return slice(size() - Suffix.Length, size()).equals(Suffix);
180c9af366fc39d657b7d416d16988265d86a641184Daniel Dunbar    }
181c9af366fc39d657b7d416d16988265d86a641184Daniel Dunbar
182f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar    /// @}
1834cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar  };
184f5fdf73238dfd923f33bcbbd397cff6752d9c41eDaniel Dunbar
1857cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  /// @name StringRef Comparison Operators
1867cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  /// @{
1877cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
1887cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  inline bool operator==(const StringRef &LHS, const StringRef &RHS) {
1897cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    return LHS.equals(RHS);
1907cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  }
1917cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
1927cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  inline bool operator!=(const StringRef &LHS, const StringRef &RHS) {
1937cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    return !(LHS == RHS);
1947cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  }
1957cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
1967cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  inline bool operator<(const StringRef &LHS, const StringRef &RHS) {
1977cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    return LHS.compare(RHS) == -1;
1987cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  }
1997cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
2007cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  inline bool operator<=(const StringRef &LHS, const StringRef &RHS) {
2017cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    return LHS.compare(RHS) != 1;
2027cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  }
2037cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
2047cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  inline bool operator>(const StringRef &LHS, const StringRef &RHS) {
2057cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    return LHS.compare(RHS) == 1;
2067cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  }
2077cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
2087cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  inline bool operator>=(const StringRef &LHS, const StringRef &RHS) {
2097cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar    return LHS.compare(RHS) != -1;
2107cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  }
2117cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
2127cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar  /// @}
2137cb6860185187c74a1205deebff9e0f09a0ddae4Daniel Dunbar
2144cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar}
2154cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar
2164cf95d75c65f37677d306952b0d2306bc6d20b1fDaniel Dunbar#endif
217