mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 19:23:23 +01:00
f0ff305346
Previously, we call write(2) for each 32767 byte chunk. That is not efficient because Linux can handle much larger write requests. This patch changes the chunk size on Linux to 1 GiB. This patch also changes the default chunks size to SSIZE_MAX. I think that doesn't in practice change this function's behavior on any operating system because SSIZE_MAX on 64-bit machine is unrealistically large, and writing 2 GiB (SSIZE_MAX on 32-bit) on a 32-bit machine by a single call of write(2) is also unrealistic, as the userspace is usually limited to 2 GiB. That said, it is in general a good thing to do because a write larger than SSIZE_MAX is implementation-defined in POSIX. Differential Revision: https://reviews.llvm.org/D39444 llvm-svn: 317015
810 lines
24 KiB
C++
810 lines
24 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_uuid(const uuid_t UUID) {
|
|
for (int Idx = 0; Idx < 16; ++Idx) {
|
|
*this << format("%02" PRIX32, UUID[Idx]);
|
|
if (Idx == 3 || Idx == 5 || Idx == 7 || Idx == 9)
|
|
*this << "-";
|
|
}
|
|
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) {
|
|
if (FS.Str.size() >= FS.Width || FS.Justify == FormattedString::JustifyNone) {
|
|
this->operator<<(FS.Str);
|
|
return *this;
|
|
}
|
|
const size_t Difference = FS.Width - FS.Str.size();
|
|
switch (FS.Justify) {
|
|
case FormattedString::JustifyLeft:
|
|
this->operator<<(FS.Str);
|
|
this->indent(Difference);
|
|
break;
|
|
case FormattedString::JustifyRight:
|
|
this->indent(Difference);
|
|
this->operator<<(FS.Str);
|
|
break;
|
|
case FormattedString::JustifyCenter: {
|
|
int PadAmount = Difference / 2;
|
|
this->indent(PadAmount);
|
|
this->operator<<(FS.Str);
|
|
this->indent(Difference - PadAmount);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Bad Justification");
|
|
}
|
|
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 and may set the "binary" flag globally based on Flags.
|
|
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) {
|
|
if (FD < 0 ) {
|
|
ShouldClose = false;
|
|
return;
|
|
}
|
|
|
|
// Do not attempt to close stdout or stderr. We used to try to maintain the
|
|
// property that tools that support writing file to stdout should not also
|
|
// write informational output to stdout, but in practice we were never able to
|
|
// maintain this invariant. Many features have been added to LLVM and clang
|
|
// (-fdump-record-layouts, optimization remarks, etc) that print to stdout, so
|
|
// users must simply be aware that mixed output and remarks is a possibility.
|
|
if (FD <= STDERR_FILENO)
|
|
ShouldClose = false;
|
|
|
|
// 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) {
|
|
if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
|
|
error_detected(EC);
|
|
}
|
|
}
|
|
|
|
#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: " + error().message(),
|
|
/*GenCrashDiag=*/false);
|
|
}
|
|
|
|
void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) {
|
|
assert(FD >= 0 && "File already closed.");
|
|
pos += Size;
|
|
|
|
// The maximum write size is limited to SSIZE_MAX because a write
|
|
// greater than SSIZE_MAX is implementation-defined in POSIX.
|
|
// Since SSIZE_MAX is not portable, we use SIZE_MAX >> 1 instead.
|
|
size_t MaxWriteSize = SIZE_MAX >> 1;
|
|
|
|
#if defined(__linux__)
|
|
// It is observed that Linux returns EINVAL for a very large write (>2G).
|
|
// Make it a reasonably small value.
|
|
MaxWriteSize = 1024 * 1024 * 1024;
|
|
#elif defined(LLVM_ON_WIN32)
|
|
// 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).
|
|
if (::_isatty(FD) && !RunningWindows8OrGreater())
|
|
MaxWriteSize = 32767;
|
|
#endif
|
|
|
|
do {
|
|
size_t ChunkSize = std::min(Size, MaxWriteSize);
|
|
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(std::error_code(errno, std::generic_category()));
|
|
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 (auto EC = sys::Process::SafelyCloseFileDescriptor(FD))
|
|
error_detected(EC);
|
|
FD = -1;
|
|
}
|
|
|
|
uint64_t raw_fd_ostream::seek(uint64_t off) {
|
|
assert(SupportsSeeking && "Stream does not support seeking!");
|
|
flush();
|
|
#ifdef LLVM_ON_WIN32
|
|
pos = ::_lseeki64(FD, off, SEEK_SET);
|
|
#elif defined(HAVE_LSEEK64)
|
|
pos = ::lseek64(FD, off, SEEK_SET);
|
|
#else
|
|
pos = ::lseek(FD, off, SEEK_SET);
|
|
#endif
|
|
if (pos == (uint64_t)-1)
|
|
error_detected(std::error_code(errno, std::generic_category()));
|
|
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.
|
|
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) {}
|