mirror of
https://github.com/RPCSX/llvm.git
synced 2024-11-27 13:40:30 +00:00
97dac2b41d
This introduces a new type-safe general purpose formatting library. It provides compile-time type safety, does not require a format specifier (since the type is deduced), and provides mechanisms for extending the format capability to user defined types, and overriding the formatting behavior for existing types. This patch additionally adds documentation for the API to the LLVM programmer's manual. Mailing List Thread: http://lists.llvm.org/pipermail/llvm-dev/2016-October/105836.html Differential Revision: https://reviews.llvm.org/D25587 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@286682 91177308-0d34-0410-b5e6-96231b3b80d8
765 lines
23 KiB
C++
765 lines
23 KiB
C++
//===--- raw_ostream.cpp - Implement the raw_ostream classes --------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This implements support for bulk buffered stream output.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/Support/Compiler.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Format.h"
|
|
#include "llvm/Support/FormatVariadic.h"
|
|
#include "llvm/Support/MathExtras.h"
|
|
#include "llvm/Support/NativeFormatting.h"
|
|
#include "llvm/Support/Process.h"
|
|
#include "llvm/Support/Program.h"
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <iterator>
|
|
#include <sys/stat.h>
|
|
#include <system_error>
|
|
|
|
// <fcntl.h> may provide O_BINARY.
|
|
#if defined(HAVE_FCNTL_H)
|
|
# include <fcntl.h>
|
|
#endif
|
|
|
|
#if defined(HAVE_UNISTD_H)
|
|
# include <unistd.h>
|
|
#endif
|
|
#if defined(HAVE_SYS_UIO_H) && defined(HAVE_WRITEV)
|
|
# include <sys/uio.h>
|
|
#endif
|
|
|
|
#if defined(__CYGWIN__)
|
|
#include <io.h>
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#include <io.h>
|
|
#ifndef STDIN_FILENO
|
|
# define STDIN_FILENO 0
|
|
#endif
|
|
#ifndef STDOUT_FILENO
|
|
# define STDOUT_FILENO 1
|
|
#endif
|
|
#ifndef STDERR_FILENO
|
|
# define STDERR_FILENO 2
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef LLVM_ON_WIN32
|
|
#include "Windows/WindowsSupport.h"
|
|
#endif
|
|
|
|
using namespace llvm;
|
|
|
|
raw_ostream::~raw_ostream() {
|
|
// raw_ostream's subclasses should take care to flush the buffer
|
|
// in their destructors.
|
|
assert(OutBufCur == OutBufStart &&
|
|
"raw_ostream destructor called with non-empty buffer!");
|
|
|
|
if (BufferMode == InternalBuffer)
|
|
delete [] OutBufStart;
|
|
}
|
|
|
|
// An out of line virtual method to provide a home for the class vtable.
|
|
void raw_ostream::handle() {}
|
|
|
|
size_t raw_ostream::preferred_buffer_size() const {
|
|
// BUFSIZ is intended to be a reasonable default.
|
|
return BUFSIZ;
|
|
}
|
|
|
|
void raw_ostream::SetBuffered() {
|
|
// Ask the subclass to determine an appropriate buffer size.
|
|
if (size_t Size = preferred_buffer_size())
|
|
SetBufferSize(Size);
|
|
else
|
|
// It may return 0, meaning this stream should be unbuffered.
|
|
SetUnbuffered();
|
|
}
|
|
|
|
void raw_ostream::SetBufferAndMode(char *BufferStart, size_t Size,
|
|
BufferKind Mode) {
|
|
assert(((Mode == Unbuffered && !BufferStart && Size == 0) ||
|
|
(Mode != Unbuffered && BufferStart && Size != 0)) &&
|
|
"stream must be unbuffered or have at least one byte");
|
|
// Make sure the current buffer is free of content (we can't flush here; the
|
|
// child buffer management logic will be in write_impl).
|
|
assert(GetNumBytesInBuffer() == 0 && "Current buffer is non-empty!");
|
|
|
|
if (BufferMode == InternalBuffer)
|
|
delete [] OutBufStart;
|
|
OutBufStart = BufferStart;
|
|
OutBufEnd = OutBufStart+Size;
|
|
OutBufCur = OutBufStart;
|
|
BufferMode = Mode;
|
|
|
|
assert(OutBufStart <= OutBufEnd && "Invalid size!");
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(unsigned long N) {
|
|
write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(long N) {
|
|
write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(unsigned long long N) {
|
|
write_integer(*this, static_cast<uint64_t>(N), 0, IntegerStyle::Integer);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(long long N) {
|
|
write_integer(*this, static_cast<int64_t>(N), 0, IntegerStyle::Integer);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::write_hex(unsigned long long N) {
|
|
llvm::write_hex(*this, N, HexPrintStyle::Lower);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::write_escaped(StringRef Str,
|
|
bool UseHexEscapes) {
|
|
for (unsigned char c : Str) {
|
|
switch (c) {
|
|
case '\\':
|
|
*this << '\\' << '\\';
|
|
break;
|
|
case '\t':
|
|
*this << '\\' << 't';
|
|
break;
|
|
case '\n':
|
|
*this << '\\' << 'n';
|
|
break;
|
|
case '"':
|
|
*this << '\\' << '"';
|
|
break;
|
|
default:
|
|
if (std::isprint(c)) {
|
|
*this << c;
|
|
break;
|
|
}
|
|
|
|
// Write out the escaped representation.
|
|
if (UseHexEscapes) {
|
|
*this << '\\' << 'x';
|
|
*this << hexdigit((c >> 4 & 0xF));
|
|
*this << hexdigit((c >> 0) & 0xF);
|
|
} else {
|
|
// Always use a full 3-character octal escape.
|
|
*this << '\\';
|
|
*this << char('0' + ((c >> 6) & 7));
|
|
*this << char('0' + ((c >> 3) & 7));
|
|
*this << char('0' + ((c >> 0) & 7));
|
|
}
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(const void *P) {
|
|
llvm::write_hex(*this, (uintptr_t)P, HexPrintStyle::PrefixLower);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(double N) {
|
|
llvm::write_double(*this, N, FloatStyle::Exponent);
|
|
return *this;
|
|
}
|
|
|
|
void raw_ostream::flush_nonempty() {
|
|
assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty.");
|
|
size_t Length = OutBufCur - OutBufStart;
|
|
OutBufCur = OutBufStart;
|
|
write_impl(OutBufStart, Length);
|
|
}
|
|
|
|
raw_ostream &raw_ostream::write(unsigned char C) {
|
|
// Group exceptional cases into a single branch.
|
|
if (LLVM_UNLIKELY(OutBufCur >= OutBufEnd)) {
|
|
if (LLVM_UNLIKELY(!OutBufStart)) {
|
|
if (BufferMode == Unbuffered) {
|
|
write_impl(reinterpret_cast<char*>(&C), 1);
|
|
return *this;
|
|
}
|
|
// Set up a buffer and start over.
|
|
SetBuffered();
|
|
return write(C);
|
|
}
|
|
|
|
flush_nonempty();
|
|
}
|
|
|
|
*OutBufCur++ = C;
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) {
|
|
// Group exceptional cases into a single branch.
|
|
if (LLVM_UNLIKELY(size_t(OutBufEnd - OutBufCur) < Size)) {
|
|
if (LLVM_UNLIKELY(!OutBufStart)) {
|
|
if (BufferMode == Unbuffered) {
|
|
write_impl(Ptr, Size);
|
|
return *this;
|
|
}
|
|
// Set up a buffer and start over.
|
|
SetBuffered();
|
|
return write(Ptr, Size);
|
|
}
|
|
|
|
size_t NumBytes = OutBufEnd - OutBufCur;
|
|
|
|
// If the buffer is empty at this point we have a string that is larger
|
|
// than the buffer. Directly write the chunk that is a multiple of the
|
|
// preferred buffer size and put the remainder in the buffer.
|
|
if (LLVM_UNLIKELY(OutBufCur == OutBufStart)) {
|
|
assert(NumBytes != 0 && "undefined behavior");
|
|
size_t BytesToWrite = Size - (Size % NumBytes);
|
|
write_impl(Ptr, BytesToWrite);
|
|
size_t BytesRemaining = Size - BytesToWrite;
|
|
if (BytesRemaining > size_t(OutBufEnd - OutBufCur)) {
|
|
// Too much left over to copy into our buffer.
|
|
return write(Ptr + BytesToWrite, BytesRemaining);
|
|
}
|
|
copy_to_buffer(Ptr + BytesToWrite, BytesRemaining);
|
|
return *this;
|
|
}
|
|
|
|
// We don't have enough space in the buffer to fit the string in. Insert as
|
|
// much as possible, flush and start over with the remainder.
|
|
copy_to_buffer(Ptr, NumBytes);
|
|
flush_nonempty();
|
|
return write(Ptr + NumBytes, Size - NumBytes);
|
|
}
|
|
|
|
copy_to_buffer(Ptr, Size);
|
|
|
|
return *this;
|
|
}
|
|
|
|
void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) {
|
|
assert(Size <= size_t(OutBufEnd - OutBufCur) && "Buffer overrun!");
|
|
|
|
// Handle short strings specially, memcpy isn't very good at very short
|
|
// strings.
|
|
switch (Size) {
|
|
case 4: OutBufCur[3] = Ptr[3]; LLVM_FALLTHROUGH;
|
|
case 3: OutBufCur[2] = Ptr[2]; LLVM_FALLTHROUGH;
|
|
case 2: OutBufCur[1] = Ptr[1]; LLVM_FALLTHROUGH;
|
|
case 1: OutBufCur[0] = Ptr[0]; LLVM_FALLTHROUGH;
|
|
case 0: break;
|
|
default:
|
|
memcpy(OutBufCur, Ptr, Size);
|
|
break;
|
|
}
|
|
|
|
OutBufCur += Size;
|
|
}
|
|
|
|
// Formatted output.
|
|
raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) {
|
|
// If we have more than a few bytes left in our output buffer, try
|
|
// formatting directly onto its end.
|
|
size_t NextBufferSize = 127;
|
|
size_t BufferBytesLeft = OutBufEnd - OutBufCur;
|
|
if (BufferBytesLeft > 3) {
|
|
size_t BytesUsed = Fmt.print(OutBufCur, BufferBytesLeft);
|
|
|
|
// Common case is that we have plenty of space.
|
|
if (BytesUsed <= BufferBytesLeft) {
|
|
OutBufCur += BytesUsed;
|
|
return *this;
|
|
}
|
|
|
|
// Otherwise, we overflowed and the return value tells us the size to try
|
|
// again with.
|
|
NextBufferSize = BytesUsed;
|
|
}
|
|
|
|
// If we got here, we didn't have enough space in the output buffer for the
|
|
// string. Try printing into a SmallVector that is resized to have enough
|
|
// space. Iterate until we win.
|
|
SmallVector<char, 128> V;
|
|
|
|
while (true) {
|
|
V.resize(NextBufferSize);
|
|
|
|
// Try formatting into the SmallVector.
|
|
size_t BytesUsed = Fmt.print(V.data(), NextBufferSize);
|
|
|
|
// If BytesUsed fit into the vector, we win.
|
|
if (BytesUsed <= NextBufferSize)
|
|
return write(V.data(), BytesUsed);
|
|
|
|
// Otherwise, try again with a new size.
|
|
assert(BytesUsed > NextBufferSize && "Didn't grow buffer!?");
|
|
NextBufferSize = BytesUsed;
|
|
}
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(const formatv_object_base &Obj) {
|
|
SmallString<128> S;
|
|
Obj.format(*this);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
|
|
unsigned Len = FS.Str.size();
|
|
int PadAmount = FS.Width - Len;
|
|
if (FS.RightJustify && (PadAmount > 0))
|
|
this->indent(PadAmount);
|
|
this->operator<<(FS.Str);
|
|
if (!FS.RightJustify && (PadAmount > 0))
|
|
this->indent(PadAmount);
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(const FormattedNumber &FN) {
|
|
if (FN.Hex) {
|
|
HexPrintStyle Style;
|
|
if (FN.Upper && FN.HexPrefix)
|
|
Style = HexPrintStyle::PrefixUpper;
|
|
else if (FN.Upper && !FN.HexPrefix)
|
|
Style = HexPrintStyle::Upper;
|
|
else if (!FN.Upper && FN.HexPrefix)
|
|
Style = HexPrintStyle::PrefixLower;
|
|
else
|
|
Style = HexPrintStyle::Lower;
|
|
llvm::write_hex(*this, FN.HexValue, Style, FN.Width);
|
|
} else {
|
|
llvm::SmallString<16> Buffer;
|
|
llvm::raw_svector_ostream Stream(Buffer);
|
|
llvm::write_integer(Stream, FN.DecValue, 0, IntegerStyle::Integer);
|
|
if (Buffer.size() < FN.Width)
|
|
indent(FN.Width - Buffer.size());
|
|
(*this) << Buffer;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) {
|
|
if (FB.Bytes.empty())
|
|
return *this;
|
|
|
|
size_t LineIndex = 0;
|
|
auto Bytes = FB.Bytes;
|
|
const size_t Size = Bytes.size();
|
|
HexPrintStyle HPS = FB.Upper ? HexPrintStyle::Upper : HexPrintStyle::Lower;
|
|
uint64_t OffsetWidth = 0;
|
|
if (FB.FirstByteOffset.hasValue()) {
|
|
// Figure out how many nibbles are needed to print the largest offset
|
|
// represented by this data set, so that we can align the offset field
|
|
// to the right width.
|
|
size_t Lines = Size / FB.NumPerLine;
|
|
uint64_t MaxOffset = *FB.FirstByteOffset + Lines * FB.NumPerLine;
|
|
unsigned Power = 0;
|
|
if (MaxOffset > 0)
|
|
Power = llvm::Log2_64_Ceil(MaxOffset);
|
|
OffsetWidth = std::max<uint64_t>(4, llvm::alignTo(Power, 4) / 4);
|
|
}
|
|
|
|
// The width of a block of data including all spaces for group separators.
|
|
unsigned NumByteGroups =
|
|
alignTo(FB.NumPerLine, FB.ByteGroupSize) / FB.ByteGroupSize;
|
|
unsigned BlockCharWidth = FB.NumPerLine * 2 + NumByteGroups - 1;
|
|
|
|
while (!Bytes.empty()) {
|
|
indent(FB.IndentLevel);
|
|
|
|
if (FB.FirstByteOffset.hasValue()) {
|
|
uint64_t Offset = FB.FirstByteOffset.getValue();
|
|
llvm::write_hex(*this, Offset + LineIndex, HPS, OffsetWidth);
|
|
*this << ": ";
|
|
}
|
|
|
|
auto Line = Bytes.take_front(FB.NumPerLine);
|
|
|
|
size_t CharsPrinted = 0;
|
|
// Print the hex bytes for this line in groups
|
|
for (size_t I = 0; I < Line.size(); ++I, CharsPrinted += 2) {
|
|
if (I && (I % FB.ByteGroupSize) == 0) {
|
|
++CharsPrinted;
|
|
*this << " ";
|
|
}
|
|
llvm::write_hex(*this, Line[I], HPS, 2);
|
|
}
|
|
|
|
if (FB.ASCII) {
|
|
// Print any spaces needed for any bytes that we didn't print on this
|
|
// line so that the ASCII bytes are correctly aligned.
|
|
assert(BlockCharWidth >= CharsPrinted);
|
|
indent(BlockCharWidth - CharsPrinted + 2);
|
|
*this << "|";
|
|
|
|
// Print the ASCII char values for each byte on this line
|
|
for (uint8_t Byte : Line) {
|
|
if (isprint(Byte))
|
|
*this << static_cast<char>(Byte);
|
|
else
|
|
*this << '.';
|
|
}
|
|
*this << '|';
|
|
}
|
|
|
|
Bytes = Bytes.drop_front(Line.size());
|
|
LineIndex += Line.size();
|
|
if (LineIndex < Size)
|
|
*this << '\n';
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// indent - Insert 'NumSpaces' spaces.
|
|
raw_ostream &raw_ostream::indent(unsigned NumSpaces) {
|
|
static const char Spaces[] = " "
|
|
" "
|
|
" ";
|
|
|
|
// Usually the indentation is small, handle it with a fastpath.
|
|
if (NumSpaces < array_lengthof(Spaces))
|
|
return write(Spaces, NumSpaces);
|
|
|
|
while (NumSpaces) {
|
|
unsigned NumToWrite = std::min(NumSpaces,
|
|
(unsigned)array_lengthof(Spaces)-1);
|
|
write(Spaces, NumToWrite);
|
|
NumSpaces -= NumToWrite;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Formatted Output
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Out of line virtual method.
|
|
void format_object_base::home() {
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// raw_fd_ostream
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static int getFD(StringRef Filename, std::error_code &EC,
|
|
sys::fs::OpenFlags Flags) {
|
|
// Handle "-" as stdout. Note that when we do this, we consider ourself
|
|
// the owner of stdout. This means that we can do things like close the
|
|
// file descriptor when we're done and set the "binary" flag globally.
|
|
if (Filename == "-") {
|
|
EC = std::error_code();
|
|
// If user requested binary then put stdout into binary mode if
|
|
// possible.
|
|
if (!(Flags & sys::fs::F_Text))
|
|
sys::ChangeStdoutToBinary();
|
|
return STDOUT_FILENO;
|
|
}
|
|
|
|
int FD;
|
|
EC = sys::fs::openFileForWrite(Filename, FD, Flags);
|
|
if (EC)
|
|
return -1;
|
|
|
|
return FD;
|
|
}
|
|
|
|
raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
|
|
sys::fs::OpenFlags Flags)
|
|
: raw_fd_ostream(getFD(Filename, EC, Flags), true) {}
|
|
|
|
/// FD is the file descriptor that this writes to. If ShouldClose is true, this
|
|
/// closes the file when the stream is destroyed.
|
|
raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered)
|
|
: raw_pwrite_stream(unbuffered), FD(fd), ShouldClose(shouldClose),
|
|
Error(false) {
|
|
if (FD < 0 ) {
|
|
ShouldClose = false;
|
|
return;
|
|
}
|
|
|
|
// Get the starting position.
|
|
off_t loc = ::lseek(FD, 0, SEEK_CUR);
|
|
#ifdef LLVM_ON_WIN32
|
|
// MSVCRT's _lseek(SEEK_CUR) doesn't return -1 for pipes.
|
|
sys::fs::file_status Status;
|
|
std::error_code EC = status(FD, Status);
|
|
SupportsSeeking = !EC && Status.type() == sys::fs::file_type::regular_file;
|
|
#else
|
|
SupportsSeeking = loc != (off_t)-1;
|
|
#endif
|
|
if (!SupportsSeeking)
|
|
pos = 0;
|
|
else
|
|
pos = static_cast<uint64_t>(loc);
|
|
}
|
|
|
|
raw_fd_ostream::~raw_fd_ostream() {
|
|
if (FD >= 0) {
|
|
flush();
|
|
if (ShouldClose && sys::Process::SafelyCloseFileDescriptor(FD))
|
|
error_detected();
|
|
}
|
|
|
|
#ifdef __MINGW32__
|
|
// On mingw, global dtors should not call exit().
|
|
// report_fatal_error() invokes exit(). We know report_fatal_error()
|
|
// might not write messages to stderr when any errors were detected
|
|
// on FD == 2.
|
|
if (FD == 2) return;
|
|
#endif
|
|
|
|
// If there are any pending errors, report them now. Clients wishing
|
|
// to avoid report_fatal_error calls should check for errors with
|
|
// has_error() and clear the error flag with clear_error() before
|
|
// destructing raw_ostream objects which may have errors.
|
|
if (has_error())
|
|
report_fatal_error("IO failure on output stream.", /*GenCrashDiag=*/false);
|
|
}
|
|
|
|
void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
|
|
assert(FD >= 0 && "File already closed.");
|
|
pos += Size;
|
|
|
|
#ifndef LLVM_ON_WIN32
|
|
bool ShouldWriteInChunks = false;
|
|
#else
|
|
// Writing a large size of output to Windows console returns ENOMEM. It seems
|
|
// that, prior to Windows 8, WriteFile() is redirecting to WriteConsole(), and
|
|
// the latter has a size limit (66000 bytes or less, depending on heap usage).
|
|
bool ShouldWriteInChunks = !!::_isatty(FD) && !RunningWindows8OrGreater();
|
|
#endif
|
|
|
|
do {
|
|
size_t ChunkSize = Size;
|
|
if (ChunkSize > 32767 && ShouldWriteInChunks)
|
|
ChunkSize = 32767;
|
|
|
|
ssize_t ret = ::write(FD, Ptr, ChunkSize);
|
|
|
|
if (ret < 0) {
|
|
// If it's a recoverable error, swallow it and retry the write.
|
|
//
|
|
// Ideally we wouldn't ever see EAGAIN or EWOULDBLOCK here, since
|
|
// raw_ostream isn't designed to do non-blocking I/O. However, some
|
|
// programs, such as old versions of bjam, have mistakenly used
|
|
// O_NONBLOCK. For compatibility, emulate blocking semantics by
|
|
// spinning until the write succeeds. If you don't want spinning,
|
|
// don't use O_NONBLOCK file descriptors with raw_ostream.
|
|
if (errno == EINTR || errno == EAGAIN
|
|
#ifdef EWOULDBLOCK
|
|
|| errno == EWOULDBLOCK
|
|
#endif
|
|
)
|
|
continue;
|
|
|
|
// Otherwise it's a non-recoverable error. Note it and quit.
|
|
error_detected();
|
|
break;
|
|
}
|
|
|
|
// The write may have written some or all of the data. Update the
|
|
// size and buffer pointer to reflect the remainder that needs
|
|
// to be written. If there are no bytes left, we're done.
|
|
Ptr += ret;
|
|
Size -= ret;
|
|
} while (Size > 0);
|
|
}
|
|
|
|
void raw_fd_ostream::close() {
|
|
assert(ShouldClose);
|
|
ShouldClose = false;
|
|
flush();
|
|
if (sys::Process::SafelyCloseFileDescriptor(FD))
|
|
error_detected();
|
|
FD = -1;
|
|
}
|
|
|
|
uint64_t raw_fd_ostream::seek(uint64_t off) {
|
|
assert(SupportsSeeking && "Stream does not support seeking!");
|
|
flush();
|
|
pos = ::lseek(FD, off, SEEK_SET);
|
|
if (pos == (uint64_t)-1)
|
|
error_detected();
|
|
return pos;
|
|
}
|
|
|
|
void raw_fd_ostream::pwrite_impl(const char *Ptr, size_t Size,
|
|
uint64_t Offset) {
|
|
uint64_t Pos = tell();
|
|
seek(Offset);
|
|
write(Ptr, Size);
|
|
seek(Pos);
|
|
}
|
|
|
|
size_t raw_fd_ostream::preferred_buffer_size() const {
|
|
#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__minix)
|
|
// Windows and Minix have no st_blksize.
|
|
assert(FD >= 0 && "File not yet open!");
|
|
struct stat statbuf;
|
|
if (fstat(FD, &statbuf) != 0)
|
|
return 0;
|
|
|
|
// If this is a terminal, don't use buffering. Line buffering
|
|
// would be a more traditional thing to do, but it's not worth
|
|
// the complexity.
|
|
if (S_ISCHR(statbuf.st_mode) && isatty(FD))
|
|
return 0;
|
|
// Return the preferred block size.
|
|
return statbuf.st_blksize;
|
|
#else
|
|
return raw_ostream::preferred_buffer_size();
|
|
#endif
|
|
}
|
|
|
|
raw_ostream &raw_fd_ostream::changeColor(enum Colors colors, bool bold,
|
|
bool bg) {
|
|
if (sys::Process::ColorNeedsFlush())
|
|
flush();
|
|
const char *colorcode =
|
|
(colors == SAVEDCOLOR) ? sys::Process::OutputBold(bg)
|
|
: sys::Process::OutputColor(colors, bold, bg);
|
|
if (colorcode) {
|
|
size_t len = strlen(colorcode);
|
|
write(colorcode, len);
|
|
// don't account colors towards output characters
|
|
pos -= len;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_fd_ostream::resetColor() {
|
|
if (sys::Process::ColorNeedsFlush())
|
|
flush();
|
|
const char *colorcode = sys::Process::ResetColor();
|
|
if (colorcode) {
|
|
size_t len = strlen(colorcode);
|
|
write(colorcode, len);
|
|
// don't account colors towards output characters
|
|
pos -= len;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
raw_ostream &raw_fd_ostream::reverseColor() {
|
|
if (sys::Process::ColorNeedsFlush())
|
|
flush();
|
|
const char *colorcode = sys::Process::OutputReverse();
|
|
if (colorcode) {
|
|
size_t len = strlen(colorcode);
|
|
write(colorcode, len);
|
|
// don't account colors towards output characters
|
|
pos -= len;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool raw_fd_ostream::is_displayed() const {
|
|
return sys::Process::FileDescriptorIsDisplayed(FD);
|
|
}
|
|
|
|
bool raw_fd_ostream::has_colors() const {
|
|
return sys::Process::FileDescriptorHasColors(FD);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// outs(), errs(), nulls()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// outs() - This returns a reference to a raw_ostream for standard output.
|
|
/// Use it like: outs() << "foo" << "bar";
|
|
raw_ostream &llvm::outs() {
|
|
// Set buffer settings to model stdout behavior. Delete the file descriptor
|
|
// when the program exits, forcing error detection. This means that if you
|
|
// ever call outs(), you can't open another raw_fd_ostream on stdout, as we'll
|
|
// close stdout twice and print an error the second time.
|
|
std::error_code EC;
|
|
static raw_fd_ostream S("-", EC, sys::fs::F_None);
|
|
assert(!EC);
|
|
return S;
|
|
}
|
|
|
|
/// errs() - This returns a reference to a raw_ostream for standard error.
|
|
/// Use it like: errs() << "foo" << "bar";
|
|
raw_ostream &llvm::errs() {
|
|
// Set standard error to be unbuffered by default.
|
|
static raw_fd_ostream S(STDERR_FILENO, false, true);
|
|
return S;
|
|
}
|
|
|
|
/// nulls() - This returns a reference to a raw_ostream which discards output.
|
|
raw_ostream &llvm::nulls() {
|
|
static raw_null_ostream S;
|
|
return S;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// raw_string_ostream
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
raw_string_ostream::~raw_string_ostream() {
|
|
flush();
|
|
}
|
|
|
|
void raw_string_ostream::write_impl(const char *Ptr, size_t Size) {
|
|
OS.append(Ptr, Size);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// raw_svector_ostream
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
uint64_t raw_svector_ostream::current_pos() const { return OS.size(); }
|
|
|
|
void raw_svector_ostream::write_impl(const char *Ptr, size_t Size) {
|
|
OS.append(Ptr, Ptr + Size);
|
|
}
|
|
|
|
void raw_svector_ostream::pwrite_impl(const char *Ptr, size_t Size,
|
|
uint64_t Offset) {
|
|
memcpy(OS.data() + Offset, Ptr, Size);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// raw_null_ostream
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
raw_null_ostream::~raw_null_ostream() {
|
|
#ifndef NDEBUG
|
|
// ~raw_ostream asserts that the buffer is empty. This isn't necessary
|
|
// with raw_null_ostream, but it's better to have raw_null_ostream follow
|
|
// the rules than to change the rules just for raw_null_ostream.
|
|
flush();
|
|
#endif
|
|
}
|
|
|
|
void raw_null_ostream::write_impl(const char *Ptr, size_t Size) {
|
|
}
|
|
|
|
uint64_t raw_null_ostream::current_pos() const {
|
|
return 0;
|
|
}
|
|
|
|
void raw_null_ostream::pwrite_impl(const char *Ptr, size_t Size,
|
|
uint64_t Offset) {}
|