Encoding.h revision 341825
190699Srobert//===--- Encoding.h - Format C++ code ---------------------------*- C++ -*-===//
290699Srobert//
390699Srobert//                     The LLVM Compiler Infrastructure
490699Srobert//
590699Srobert// This file is distributed under the University of Illinois Open Source
690699Srobert// License. See LICENSE.TXT for details.
790699Srobert//
890699Srobert//===----------------------------------------------------------------------===//
990699Srobert///
1090699Srobert/// \file
1190699Srobert/// Contains functions for text encoding manipulation. Supports UTF-8,
1290699Srobert/// 8-bit encodings and escape sequences in C++ string literals.
1390699Srobert///
1490699Srobert//===----------------------------------------------------------------------===//
1590699Srobert
1690699Srobert#ifndef LLVM_CLANG_LIB_FORMAT_ENCODING_H
1790699Srobert#define LLVM_CLANG_LIB_FORMAT_ENCODING_H
1890699Srobert
1990699Srobert#include "clang/Basic/LLVM.h"
2090699Srobert#include "llvm/ADT/StringRef.h"
2190699Srobert#include "llvm/Support/ConvertUTF.h"
2290699Srobert#include "llvm/Support/Unicode.h"
2390699Srobert
2490699Srobertnamespace clang {
2590699Srobertnamespace format {
2690699Srobertnamespace encoding {
2790699Srobert
2890699Srobertenum Encoding {
2990699Srobert  Encoding_UTF8,
3090699Srobert  Encoding_Unknown // We treat all other encodings as 8-bit encodings.
3190699Srobert};
3290699Srobert
3390699Srobert/// Detects encoding of the Text. If the Text can be decoded using UTF-8,
3490699Srobert/// it is considered UTF8, otherwise we treat it as some 8-bit encoding.
3590699Srobertinline Encoding detectEncoding(StringRef Text) {
3690699Srobert  const llvm::UTF8 *Ptr = reinterpret_cast<const llvm::UTF8 *>(Text.begin());
3790699Srobert  const llvm::UTF8 *BufEnd = reinterpret_cast<const llvm::UTF8 *>(Text.end());
3890699Srobert  if (llvm::isLegalUTF8String(&Ptr, BufEnd))
3990699Srobert    return Encoding_UTF8;
4090699Srobert  return Encoding_Unknown;
4190699Srobert}
4290699Srobert
4390699Srobert/// Returns the number of columns required to display the \p Text on a
4490699Srobert/// generic Unicode-capable terminal. Text is assumed to use the specified
4590699Srobert/// \p Encoding.
4690699Srobertinline unsigned columnWidth(StringRef Text, Encoding Encoding) {
4790699Srobert  if (Encoding == Encoding_UTF8) {
4890699Srobert    int ContentWidth = llvm::sys::unicode::columnWidthUTF8(Text);
4990699Srobert    // FIXME: Figure out the correct way to handle this in the presence of both
5090699Srobert    // printable and unprintable multi-byte UTF-8 characters. Falling back to
5190699Srobert    // returning the number of bytes may cause problems, as columnWidth suddenly
5290699Srobert    // becomes non-additive.
5390699Srobert    if (ContentWidth >= 0)
5490699Srobert      return ContentWidth;
5590699Srobert  }
5690699Srobert  return Text.size();
5790699Srobert}
5890699Srobert
5990699Srobert/// Returns the number of columns required to display the \p Text,
6090699Srobert/// starting from the \p StartColumn on a terminal with the \p TabWidth. The
6190699Srobert/// text is assumed to use the specified \p Encoding.
6290699Srobertinline unsigned columnWidthWithTabs(StringRef Text, unsigned StartColumn,
6390699Srobert                                    unsigned TabWidth, Encoding Encoding) {
6490699Srobert  unsigned TotalWidth = 0;
6590699Srobert  StringRef Tail = Text;
6690699Srobert  for (;;) {
6790699Srobert    StringRef::size_type TabPos = Tail.find('\t');
6890699Srobert    if (TabPos == StringRef::npos)
6990699Srobert      return TotalWidth + columnWidth(Tail, Encoding);
7090699Srobert    TotalWidth += columnWidth(Tail.substr(0, TabPos), Encoding);
7190699Srobert    TotalWidth += TabWidth - (TotalWidth + StartColumn) % TabWidth;
7290699Srobert    Tail = Tail.substr(TabPos + 1);
7390699Srobert  }
7490699Srobert}
7590699Srobert
7690699Srobert/// Gets the number of bytes in a sequence representing a single
7790699Srobert/// codepoint and starting with FirstChar in the specified Encoding.
7890699Srobertinline unsigned getCodePointNumBytes(char FirstChar, Encoding Encoding) {
7990699Srobert  switch (Encoding) {
8090699Srobert  case Encoding_UTF8:
8190699Srobert    return llvm::getNumBytesForUTF8(FirstChar);
8290699Srobert  default:
8390699Srobert    return 1;
8490699Srobert  }
8590699Srobert}
8690699Srobert
8790699Srobertinline bool isOctDigit(char c) { return '0' <= c && c <= '7'; }
8890699Srobert
8990699Srobertinline bool isHexDigit(char c) {
9090699Srobert  return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
9190699Srobert         ('A' <= c && c <= 'F');
9290699Srobert}
9390699Srobert
9490699Srobert/// Gets the length of an escape sequence inside a C++ string literal.
9590699Srobert/// Text should span from the beginning of the escape sequence (starting with a
9690699Srobert/// backslash) to the end of the string literal.
9790699Srobertinline unsigned getEscapeSequenceLength(StringRef Text) {
9890699Srobert  assert(Text[0] == '\\');
9990699Srobert  if (Text.size() < 2)
10090699Srobert    return 1;
10190699Srobert
10290699Srobert  switch (Text[1]) {
10390699Srobert  case 'u':
10490699Srobert    return 6;
10590699Srobert  case 'U':
10690699Srobert    return 10;
10790699Srobert  case 'x': {
10890699Srobert    unsigned I = 2; // Point after '\x'.
10990699Srobert    while (I < Text.size() && isHexDigit(Text[I]))
11090699Srobert      ++I;
11190699Srobert    return I;
11290699Srobert  }
11390699Srobert  default:
11490699Srobert    if (isOctDigit(Text[1])) {
11590699Srobert      unsigned I = 1;
11690699Srobert      while (I < Text.size() && I < 4 && isOctDigit(Text[I]))
11790699Srobert        ++I;
11890699Srobert      return I;
11990699Srobert    }
12090699Srobert    return 1 + llvm::getNumBytesForUTF8(Text[1]);
12190699Srobert  }
12290699Srobert}
12390699Srobert
12490699Srobert} // namespace encoding
12590699Srobert} // namespace format
12690699Srobert} // namespace clang
12790699Srobert
12890699Srobert#endif
12990699Srobert