FormattedStream.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. //===-- llvm/Support/FormattedStream.cpp - Formatted streams ----*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file contains the implementation of formatted_raw_ostream.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "llvm/Support/FormattedStream.h"
  13. #include "llvm/Support/ConvertUTF.h"
  14. #include "llvm/Support/Debug.h"
  15. #include "llvm/Support/Unicode.h"
  16. #include "llvm/Support/raw_ostream.h"
  17. #include <algorithm>
  18. using namespace llvm;
  19. /// UpdatePosition - Examine the given char sequence and figure out which
  20. /// column we end up in after output, and how many line breaks are contained.
  21. /// This assumes that the input string is well-formed UTF-8, and takes into
  22. /// account Unicode characters which render as multiple columns wide.
  23. void formatted_raw_ostream::UpdatePosition(const char *Ptr, size_t Size) {
  24. unsigned &Column = Position.first;
  25. unsigned &Line = Position.second;
  26. auto ProcessUTF8CodePoint = [&Line, &Column](StringRef CP) {
  27. int Width = sys::unicode::columnWidthUTF8(CP);
  28. if (Width != sys::unicode::ErrorNonPrintableCharacter)
  29. Column += Width;
  30. // The only special whitespace characters we care about are single-byte.
  31. if (CP.size() > 1)
  32. return;
  33. switch (CP[0]) {
  34. case '\n':
  35. Line += 1;
  36. [[fallthrough]];
  37. case '\r':
  38. Column = 0;
  39. break;
  40. case '\t':
  41. // Assumes tab stop = 8 characters.
  42. Column += (8 - (Column & 0x7)) & 0x7;
  43. break;
  44. }
  45. };
  46. // If we have a partial UTF-8 sequence from the previous buffer, check that
  47. // first.
  48. if (PartialUTF8Char.size()) {
  49. size_t BytesFromBuffer =
  50. getNumBytesForUTF8(PartialUTF8Char[0]) - PartialUTF8Char.size();
  51. if (Size < BytesFromBuffer) {
  52. // If we still don't have enough bytes for a complete code point, just
  53. // append what we have.
  54. PartialUTF8Char.append(StringRef(Ptr, Size));
  55. return;
  56. } else {
  57. // The first few bytes from the buffer will complete the code point.
  58. // Concatenate them and process their effect on the line and column
  59. // numbers.
  60. PartialUTF8Char.append(StringRef(Ptr, BytesFromBuffer));
  61. ProcessUTF8CodePoint(PartialUTF8Char);
  62. PartialUTF8Char.clear();
  63. Ptr += BytesFromBuffer;
  64. Size -= BytesFromBuffer;
  65. }
  66. }
  67. // Now scan the rest of the buffer.
  68. unsigned NumBytes;
  69. for (const char *End = Ptr + Size; Ptr < End; Ptr += NumBytes) {
  70. NumBytes = getNumBytesForUTF8(*Ptr);
  71. // The buffer might end part way through a UTF-8 code unit sequence for a
  72. // Unicode scalar value if it got flushed. If this happens, we can't know
  73. // the display width until we see the rest of the code point. Stash the
  74. // bytes we do have, so that we can reconstruct the whole code point later,
  75. // even if the buffer is being flushed.
  76. if ((unsigned)(End - Ptr) < NumBytes) {
  77. PartialUTF8Char = StringRef(Ptr, End - Ptr);
  78. return;
  79. }
  80. ProcessUTF8CodePoint(StringRef(Ptr, NumBytes));
  81. }
  82. }
  83. /// ComputePosition - Examine the current output and update line and column
  84. /// counts.
  85. void formatted_raw_ostream::ComputePosition(const char *Ptr, size_t Size) {
  86. // If our previous scan pointer is inside the buffer, assume we already
  87. // scanned those bytes. This depends on raw_ostream to not change our buffer
  88. // in unexpected ways.
  89. if (Ptr <= Scanned && Scanned <= Ptr + Size)
  90. // Scan all characters added since our last scan to determine the new
  91. // column.
  92. UpdatePosition(Scanned, Size - (Scanned - Ptr));
  93. else
  94. UpdatePosition(Ptr, Size);
  95. // Update the scanning pointer.
  96. Scanned = Ptr + Size;
  97. }
  98. /// PadToColumn - Align the output to some column number.
  99. ///
  100. /// \param NewCol - The column to move to.
  101. ///
  102. formatted_raw_ostream &formatted_raw_ostream::PadToColumn(unsigned NewCol) {
  103. // Figure out what's in the buffer and add it to the column count.
  104. ComputePosition(getBufferStart(), GetNumBytesInBuffer());
  105. // Output spaces until we reach the desired column.
  106. indent(std::max(int(NewCol - getColumn()), 1));
  107. return *this;
  108. }
  109. void formatted_raw_ostream::write_impl(const char *Ptr, size_t Size) {
  110. // Figure out what's in the buffer and add it to the column count.
  111. ComputePosition(Ptr, Size);
  112. // Write the data to the underlying stream (which is unbuffered, so
  113. // the data will be immediately written out).
  114. TheStream->write(Ptr, Size);
  115. // Reset the scanning pointer.
  116. Scanned = nullptr;
  117. }
  118. /// fouts() - This returns a reference to a formatted_raw_ostream for
  119. /// standard output. Use it like: fouts() << "foo" << "bar";
  120. formatted_raw_ostream &llvm::fouts() {
  121. static formatted_raw_ostream S(outs());
  122. return S;
  123. }
  124. /// ferrs() - This returns a reference to a formatted_raw_ostream for
  125. /// standard error. Use it like: ferrs() << "foo" << "bar";
  126. formatted_raw_ostream &llvm::ferrs() {
  127. static formatted_raw_ostream S(errs());
  128. return S;
  129. }
  130. /// fdbgs() - This returns a reference to a formatted_raw_ostream for
  131. /// the debug stream. Use it like: fdbgs() << "foo" << "bar";
  132. formatted_raw_ostream &llvm::fdbgs() {
  133. static formatted_raw_ostream S(dbgs());
  134. return S;
  135. }