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 13 #include "llvm/Support/FormattedStream.h" 14 #include "llvm/Support/ConvertUTF.h" 15 #include "llvm/Support/Debug.h" 16 #include "llvm/Support/Locale.h" 17 #include "llvm/Support/raw_ostream.h" 18 #include <algorithm> 19 20 using namespace llvm; 21 22 /// UpdatePosition - Examine the given char sequence and figure out which 23 /// column we end up in after output, and how many line breaks are contained. 24 /// This assumes that the input string is well-formed UTF-8, and takes into 25 /// account unicode characters which render as multiple columns wide. 26 static void UpdatePosition(std::pair<unsigned, unsigned> &Position, 27 const char *Ptr, size_t Size) { 28 unsigned &Column = Position.first; 29 unsigned &Line = Position.second; 30 31 // Keep track of the current column and line by scanning the string for 32 // special characters. 33 unsigned NumBytes; 34 for (const char *End = Ptr + Size; Ptr < End; Ptr += NumBytes) { 35 NumBytes = getNumBytesForUTF8(*Ptr); 36 37 // The string should never end part way through a multi-byte sequence. 38 assert((Ptr + NumBytes) <= End && "Malformed multi-byte sequence"); 39 40 int Width = sys::locale::columnWidth(StringRef(Ptr, NumBytes)); 41 // columnWidth returns -1 for non-printing characters. 42 if (Width != -1) 43 Column += Width; 44 45 // If this is the final byte of a multi-byte sequence, it can't be any of 46 // the special whitespace characters below. 47 if (NumBytes > 1) 48 continue; 49 50 switch (*Ptr) { 51 case '\n': 52 Line += 1; 53 LLVM_FALLTHROUGH; 54 case '\r': 55 Column = 0; 56 break; 57 case '\t': 58 // Assumes tab stop = 8 characters. 59 Column += (8 - (Column & 0x7)) & 0x7; 60 break; 61 } 62 } 63 } 64 65 /// ComputePosition - Examine the current output and update line and column 66 /// counts. 67 void formatted_raw_ostream::ComputePosition(const char *Ptr, size_t Size) { 68 // If our previous scan pointer is inside the buffer, assume we already 69 // scanned those bytes. This depends on raw_ostream to not change our buffer 70 // in unexpected ways. 71 if (Ptr <= Scanned && Scanned <= Ptr + Size) 72 // Scan all characters added since our last scan to determine the new 73 // column. 74 UpdatePosition(Position, Scanned, Size - (Scanned - Ptr)); 75 else 76 UpdatePosition(Position, Ptr, Size); 77 78 // Update the scanning pointer. 79 Scanned = Ptr + Size; 80 } 81 82 /// PadToColumn - Align the output to some column number. 83 /// 84 /// \param NewCol - The column to move to. 85 /// 86 formatted_raw_ostream &formatted_raw_ostream::PadToColumn(unsigned NewCol) { 87 // Figure out what's in the buffer and add it to the column count. 88 ComputePosition(getBufferStart(), GetNumBytesInBuffer()); 89 90 // Output spaces until we reach the desired column. 91 indent(std::max(int(NewCol - getColumn()), 1)); 92 return *this; 93 } 94 95 void formatted_raw_ostream::write_impl(const char *Ptr, size_t Size) { 96 // Figure out what's in the buffer and add it to the column count. 97 ComputePosition(Ptr, Size); 98 99 // Write the data to the underlying stream (which is unbuffered, so 100 // the data will be immediately written out). 101 TheStream->write(Ptr, Size); 102 103 // Reset the scanning pointer. 104 Scanned = nullptr; 105 } 106 107 /// fouts() - This returns a reference to a formatted_raw_ostream for 108 /// standard output. Use it like: fouts() << "foo" << "bar"; 109 formatted_raw_ostream &llvm::fouts() { 110 static formatted_raw_ostream S(outs()); 111 return S; 112 } 113 114 /// ferrs() - This returns a reference to a formatted_raw_ostream for 115 /// standard error. Use it like: ferrs() << "foo" << "bar"; 116 formatted_raw_ostream &llvm::ferrs() { 117 static formatted_raw_ostream S(errs()); 118 return S; 119 } 120 121 /// fdbgs() - This returns a reference to a formatted_raw_ostream for 122 /// the debug stream. Use it like: fdbgs() << "foo" << "bar"; 123 formatted_raw_ostream &llvm::fdbgs() { 124 static formatted_raw_ostream S(dbgs()); 125 return S; 126 } 127