Update LLVM from stable upstream (#1653)

Replace CheckedMalloc with upstream safe_malloc.
This commit is contained in:
Peter Johnson
2019-04-27 20:33:08 -07:00
committed by GitHub
parent 3cf4f38f5d
commit 2de3bf7f58
59 changed files with 4839 additions and 841 deletions

View File

@@ -7,9 +7,9 @@
#include "HttpCameraImpl.h"
#include <wpi/MemAlloc.h>
#include <wpi/STLExtras.h>
#include <wpi/TCPConnector.h>
#include <wpi/memory.h>
#include <wpi/timestamp.h>
#include "Handle.h"
@@ -611,7 +611,7 @@ void CS_SetHttpCameraUrls(CS_Source source, const char** urls, int count,
char** CS_GetHttpCameraUrls(CS_Source source, int* count, CS_Status* status) {
auto urls = cs::GetHttpCameraUrls(source, status);
char** out =
static_cast<char**>(wpi::CheckedMalloc(urls.size() * sizeof(char*)));
static_cast<char**>(wpi::safe_malloc(urls.size() * sizeof(char*)));
*count = urls.size();
for (size_t i = 0; i < urls.size(); ++i) out[i] = cs::ConvertToC(urls[i]);
return out;

View File

@@ -17,7 +17,7 @@ static void ConvertToC(CS_UsbCameraInfo* out, const UsbCameraInfo& in) {
out->path = ConvertToC(in.path);
out->name = ConvertToC(in.name);
out->otherPaths = static_cast<char**>(
wpi::CheckedMalloc(in.otherPaths.size() * sizeof(char*)));
wpi::safe_malloc(in.otherPaths.size() * sizeof(char*)));
out->otherPathsCount = in.otherPaths.size();
for (size_t i = 0; i < in.otherPaths.size(); ++i)
out->otherPaths[i] = cs::ConvertToC(in.otherPaths[i]);
@@ -50,7 +50,7 @@ CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status) {
auto info = cs::GetUsbCameraInfo(source, status);
if (*status != CS_OK) return nullptr;
CS_UsbCameraInfo* out = static_cast<CS_UsbCameraInfo*>(
wpi::CheckedMalloc(sizeof(CS_UsbCameraInfo)));
wpi::safe_malloc(sizeof(CS_UsbCameraInfo)));
ConvertToC(out, info);
return out;
}
@@ -58,7 +58,7 @@ CS_UsbCameraInfo* CS_GetUsbCameraInfo(CS_Source source, CS_Status* status) {
CS_UsbCameraInfo* CS_EnumerateUsbCameras(int* count, CS_Status* status) {
auto cameras = cs::EnumerateUsbCameras(status);
CS_UsbCameraInfo* out = static_cast<CS_UsbCameraInfo*>(
wpi::CheckedMalloc(cameras.size() * sizeof(CS_UsbCameraInfo)));
wpi::safe_malloc(cameras.size() * sizeof(CS_UsbCameraInfo)));
*count = cameras.size();
for (size_t i = 0; i < cameras.size(); ++i) ConvertToC(&out[i], cameras[i]);
return out;

View File

@@ -11,13 +11,13 @@
#include <cstdlib>
#include <cstring>
#include <wpi/MemAlloc.h>
#include <wpi/StringRef.h>
#include <wpi/memory.h>
namespace cs {
inline char* ConvertToC(wpi::StringRef in) {
char* out = static_cast<char*>(wpi::CheckedMalloc(in.size() + 1));
char* out = static_cast<char*>(wpi::safe_malloc(in.size() + 1));
std::memmove(out, in.data(), in.size());
out[in.size()] = '\0';
return out;

View File

@@ -11,8 +11,8 @@
#include <cstdlib>
#include <opencv2/core/core.hpp>
#include <wpi/MemAlloc.h>
#include <wpi/SmallString.h>
#include <wpi/memory.h>
#include "c_util.h"
#include "cscore_cpp.h"
@@ -70,7 +70,7 @@ char** CS_GetEnumPropertyChoices(CS_Property property, int* count,
CS_Status* status) {
auto choices = cs::GetEnumPropertyChoices(property, status);
char** out =
static_cast<char**>(wpi::CheckedMalloc(choices.size() * sizeof(char*)));
static_cast<char**>(wpi::safe_malloc(choices.size() * sizeof(char*)));
*count = choices.size();
for (size_t i = 0; i < choices.size(); ++i)
out[i] = cs::ConvertToC(choices[i]);
@@ -123,7 +123,7 @@ CS_Property* CS_EnumerateSourceProperties(CS_Source source, int* count,
wpi::SmallVector<CS_Property, 32> buf;
auto vec = cs::EnumerateSourceProperties(source, buf, status);
CS_Property* out = static_cast<CS_Property*>(
wpi::CheckedMalloc(vec.size() * sizeof(CS_Property)));
wpi::safe_malloc(vec.size() * sizeof(CS_Property)));
*count = vec.size();
std::copy(vec.begin(), vec.end(), out);
return out;
@@ -183,7 +183,7 @@ CS_VideoMode* CS_EnumerateSourceVideoModes(CS_Source source, int* count,
CS_Status* status) {
auto vec = cs::EnumerateSourceVideoModes(source, status);
CS_VideoMode* out = static_cast<CS_VideoMode*>(
wpi::CheckedMalloc(vec.size() * sizeof(CS_VideoMode)));
wpi::safe_malloc(vec.size() * sizeof(CS_VideoMode)));
*count = vec.size();
std::copy(vec.begin(), vec.end(), out);
return out;
@@ -193,8 +193,8 @@ CS_Sink* CS_EnumerateSourceSinks(CS_Source source, int* count,
CS_Status* status) {
wpi::SmallVector<CS_Sink, 32> buf;
auto handles = cs::EnumerateSourceSinks(source, buf, status);
CS_Sink* sinks = static_cast<CS_Sink*>(
wpi::CheckedMalloc(handles.size() * sizeof(CS_Sink)));
CS_Sink* sinks =
static_cast<CS_Sink*>(wpi::safe_malloc(handles.size() * sizeof(CS_Sink)));
*count = handles.size();
std::copy(handles.begin(), handles.end(), sinks);
return sinks;
@@ -271,7 +271,7 @@ CS_Property* CS_EnumerateSinkProperties(CS_Sink sink, int* count,
wpi::SmallVector<CS_Property, 32> buf;
auto vec = cs::EnumerateSinkProperties(sink, buf, status);
CS_Property* out = static_cast<CS_Property*>(
wpi::CheckedMalloc(vec.size() * sizeof(CS_Property)));
wpi::safe_malloc(vec.size() * sizeof(CS_Property)));
*count = vec.size();
std::copy(vec.begin(), vec.end(), out);
return out;
@@ -372,7 +372,7 @@ CS_Source* CS_EnumerateSources(int* count, CS_Status* status) {
wpi::SmallVector<CS_Source, 32> buf;
auto handles = cs::EnumerateSourceHandles(buf, status);
CS_Source* sources = static_cast<CS_Source*>(
wpi::CheckedMalloc(handles.size() * sizeof(CS_Source)));
wpi::safe_malloc(handles.size() * sizeof(CS_Source)));
*count = handles.size();
std::copy(handles.begin(), handles.end(), sources);
return sources;
@@ -390,8 +390,8 @@ void CS_ReleaseEnumeratedSources(CS_Source* sources, int count) {
CS_Sink* CS_EnumerateSinks(int* count, CS_Status* status) {
wpi::SmallVector<CS_Sink, 32> buf;
auto handles = cs::EnumerateSinkHandles(buf, status);
CS_Sink* sinks = static_cast<CS_Sink*>(
wpi::CheckedMalloc(handles.size() * sizeof(CS_Sink)));
CS_Sink* sinks =
static_cast<CS_Sink*>(wpi::safe_malloc(handles.size() * sizeof(CS_Sink)));
*count = handles.size();
std::copy(handles.begin(), handles.end(), sinks);
return sinks;
@@ -426,8 +426,8 @@ char* CS_GetHostname() { return cs::ConvertToC(cs::GetHostname()); }
char** CS_GetNetworkInterfaces(int* count) {
auto interfaces = cs::GetNetworkInterfaces();
char** out = static_cast<char**>(
wpi::CheckedMalloc(interfaces.size() * sizeof(char*)));
char** out =
static_cast<char**>(wpi::safe_malloc(interfaces.size() * sizeof(char*)));
*count = interfaces.size();
for (size_t i = 0; i < interfaces.size(); ++i)
out[i] = cs::ConvertToC(interfaces[i]);

View File

@@ -25,9 +25,9 @@
#include <algorithm>
#include <wpi/FileSystem.h>
#include <wpi/MemAlloc.h>
#include <wpi/Path.h>
#include <wpi/SmallString.h>
#include <wpi/memory.h>
#include <wpi/raw_ostream.h>
#include <wpi/timestamp.h>

View File

@@ -28,8 +28,8 @@
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <wpi/MemAlloc.h>
#include <wpi/SmallString.h>
#include <wpi/memory.h>
#include <wpi/raw_ostream.h>
#include <wpi/timestamp.h>

View File

@@ -7,7 +7,7 @@
#include <stdint.h>
#include <wpi/memory.h>
#include <wpi/MemAlloc.h>
#include <wpi/timestamp.h>
#include "Value_internal.h"
@@ -123,7 +123,7 @@ void nt::ConvertToC(const Value& in, NT_Value* out) {
case NT_BOOLEAN_ARRAY: {
auto v = in.GetBooleanArray();
out->data.arr_boolean.arr =
static_cast<int*>(wpi::CheckedMalloc(v.size() * sizeof(int)));
static_cast<int*>(wpi::safe_malloc(v.size() * sizeof(int)));
out->data.arr_boolean.size = v.size();
std::copy(v.begin(), v.end(), out->data.arr_boolean.arr);
break;
@@ -131,7 +131,7 @@ void nt::ConvertToC(const Value& in, NT_Value* out) {
case NT_DOUBLE_ARRAY: {
auto v = in.GetDoubleArray();
out->data.arr_double.arr =
static_cast<double*>(wpi::CheckedMalloc(v.size() * sizeof(double)));
static_cast<double*>(wpi::safe_malloc(v.size() * sizeof(double)));
out->data.arr_double.size = v.size();
std::copy(v.begin(), v.end(), out->data.arr_double.arr);
break;
@@ -139,7 +139,7 @@ void nt::ConvertToC(const Value& in, NT_Value* out) {
case NT_STRING_ARRAY: {
auto v = in.GetStringArray();
out->data.arr_string.arr = static_cast<NT_String*>(
wpi::CheckedMalloc(v.size() * sizeof(NT_String)));
wpi::safe_malloc(v.size() * sizeof(NT_String)));
for (size_t i = 0; i < v.size(); ++i)
ConvertToC(v[i], &out->data.arr_string.arr[i]);
out->data.arr_string.size = v.size();
@@ -154,7 +154,7 @@ void nt::ConvertToC(const Value& in, NT_Value* out) {
void nt::ConvertToC(wpi::StringRef in, NT_String* out) {
out->len = in.size();
out->str = static_cast<char*>(wpi::CheckedMalloc(in.size() + 1));
out->str = static_cast<char*>(wpi::safe_malloc(in.size() + 1));
std::memcpy(out->str, in.data(), in.size());
out->str[in.size()] = '\0';
}

View File

@@ -14,8 +14,8 @@
#include <cstring>
#include <wpi/MathExtras.h>
#include <wpi/MemAlloc.h>
#include <wpi/leb128.h>
#include <wpi/memory.h>
using namespace nt;
@@ -53,7 +53,7 @@ WireDecoder::WireDecoder(wpi::raw_istream& is, unsigned int proto_rev,
// Start with a 1K temporary buffer. Use malloc instead of new so we can
// realloc.
m_allocated = 1024;
m_buf = static_cast<char*>(wpi::CheckedMalloc(m_allocated));
m_buf = static_cast<char*>(wpi::safe_malloc(m_allocated));
m_proto_rev = proto_rev;
m_error = nullptr;
}
@@ -72,7 +72,7 @@ void WireDecoder::Realloc(size_t len) {
if (m_allocated >= len) return;
size_t newlen = m_allocated * 2;
while (newlen < len) newlen *= 2;
m_buf = static_cast<char*>(wpi::CheckedRealloc(m_buf, newlen));
m_buf = static_cast<char*>(wpi::safe_realloc(m_buf, newlen));
m_allocated = newlen;
}

View File

@@ -10,7 +10,7 @@
#include <cassert>
#include <cstdlib>
#include <wpi/memory.h>
#include <wpi/MemAlloc.h>
#include <wpi/timestamp.h>
#include "Value_internal.h"
@@ -21,7 +21,7 @@ using namespace nt;
// Conversion helpers
static void ConvertToC(wpi::StringRef in, char** out) {
*out = static_cast<char*>(wpi::CheckedMalloc(in.size() + 1));
*out = static_cast<char*>(wpi::safe_malloc(in.size() + 1));
std::memmove(*out, in.data(), in.size());
(*out)[in.size()] = '\0';
}
@@ -58,13 +58,13 @@ static void ConvertToC(const RpcDefinition& in, NT_RpcDefinition* out) {
out->num_params = in.params.size();
out->params = static_cast<NT_RpcParamDef*>(
wpi::CheckedMalloc(in.params.size() * sizeof(NT_RpcParamDef)));
wpi::safe_malloc(in.params.size() * sizeof(NT_RpcParamDef)));
for (size_t i = 0; i < in.params.size(); ++i)
ConvertToC(in.params[i], &out->params[i]);
out->num_results = in.results.size();
out->results = static_cast<NT_RpcResultDef*>(
wpi::CheckedMalloc(in.results.size() * sizeof(NT_RpcResultDef)));
wpi::safe_malloc(in.results.size() * sizeof(NT_RpcResultDef)));
for (size_t i = 0; i < in.results.size(); ++i)
ConvertToC(in.results[i], &out->results[i]);
}
@@ -105,7 +105,7 @@ static O* ConvertToC(const std::vector<I>& in, size_t* out_len) {
if (!out_len) return nullptr;
*out_len = in.size();
if (in.empty()) return nullptr;
O* out = static_cast<O*>(wpi::CheckedMalloc(sizeof(O) * in.size()));
O* out = static_cast<O*>(wpi::safe_malloc(sizeof(O) * in.size()));
for (size_t i = 0; i < in.size(); ++i) ConvertToC(in[i], &out[i]);
return out;
}
@@ -188,7 +188,7 @@ NT_Entry* NT_GetEntries(NT_Inst inst, const char* prefix, size_t prefix_len,
// create array and copy into it
NT_Entry* info = static_cast<NT_Entry*>(
wpi::CheckedMalloc(info_v.size() * sizeof(NT_Entry)));
wpi::safe_malloc(info_v.size() * sizeof(NT_Entry)));
std::memcpy(info, info_v.data(), info_v.size() * sizeof(NT_Entry));
return info;
}
@@ -519,9 +519,9 @@ NT_Value** NT_UnpackRpcValues(const char* packed, size_t packed_len,
// create array and copy into it
NT_Value** values = static_cast<NT_Value**>(
wpi::CheckedMalloc(values_v.size() * sizeof(NT_Value*)));
wpi::safe_malloc(values_v.size() * sizeof(NT_Value*)));
for (size_t i = 0; i < values_v.size(); ++i) {
values[i] = static_cast<NT_Value*>(wpi::CheckedMalloc(sizeof(NT_Value)));
values[i] = static_cast<NT_Value*>(wpi::safe_malloc(sizeof(NT_Value)));
ConvertToC(*values_v[i], values[i]);
}
return values;
@@ -802,27 +802,27 @@ void NT_DisposeRpcAnswer(NT_RpcAnswer* call_info) {
/* Allocates a char array of the specified size.*/
char* NT_AllocateCharArray(size_t size) {
char* retVal = static_cast<char*>(wpi::CheckedMalloc(size * sizeof(char)));
char* retVal = static_cast<char*>(wpi::safe_malloc(size * sizeof(char)));
return retVal;
}
/* Allocates an integer or boolean array of the specified size. */
int* NT_AllocateBooleanArray(size_t size) {
int* retVal = static_cast<int*>(wpi::CheckedMalloc(size * sizeof(int)));
int* retVal = static_cast<int*>(wpi::safe_malloc(size * sizeof(int)));
return retVal;
}
/* Allocates a double array of the specified size. */
double* NT_AllocateDoubleArray(size_t size) {
double* retVal =
static_cast<double*>(wpi::CheckedMalloc(size * sizeof(double)));
static_cast<double*>(wpi::safe_malloc(size * sizeof(double)));
return retVal;
}
/* Allocates an NT_String array of the specified size. */
struct NT_String* NT_AllocateStringArray(size_t size) {
NT_String* retVal =
static_cast<NT_String*>(wpi::CheckedMalloc(size * sizeof(NT_String)));
static_cast<NT_String*>(wpi::safe_malloc(size * sizeof(NT_String)));
return retVal;
}
@@ -944,7 +944,7 @@ char* NT_GetValueString(const struct NT_Value* value, uint64_t* last_change,
*last_change = value->last_change;
*str_len = value->data.v_string.len;
char* str =
static_cast<char*>(wpi::CheckedMalloc(value->data.v_string.len + 1));
static_cast<char*>(wpi::safe_malloc(value->data.v_string.len + 1));
std::memcpy(str, value->data.v_string.str, value->data.v_string.len + 1);
return str;
}
@@ -955,7 +955,7 @@ char* NT_GetValueRaw(const struct NT_Value* value, uint64_t* last_change,
*last_change = value->last_change;
*raw_len = value->data.v_string.len;
char* raw =
static_cast<char*>(wpi::CheckedMalloc(value->data.v_string.len + 1));
static_cast<char*>(wpi::safe_malloc(value->data.v_string.len + 1));
std::memcpy(raw, value->data.v_string.str, value->data.v_string.len + 1);
return raw;
}
@@ -966,7 +966,7 @@ NT_Bool* NT_GetValueBooleanArray(const struct NT_Value* value,
*last_change = value->last_change;
*arr_size = value->data.arr_boolean.size;
NT_Bool* arr = static_cast<int*>(
wpi::CheckedMalloc(value->data.arr_boolean.size * sizeof(NT_Bool)));
wpi::safe_malloc(value->data.arr_boolean.size * sizeof(NT_Bool)));
std::memcpy(arr, value->data.arr_boolean.arr,
value->data.arr_boolean.size * sizeof(NT_Bool));
return arr;
@@ -978,7 +978,7 @@ double* NT_GetValueDoubleArray(const struct NT_Value* value,
*last_change = value->last_change;
*arr_size = value->data.arr_double.size;
double* arr = static_cast<double*>(
wpi::CheckedMalloc(value->data.arr_double.size * sizeof(double)));
wpi::safe_malloc(value->data.arr_double.size * sizeof(double)));
std::memcpy(arr, value->data.arr_double.arr,
value->data.arr_double.size * sizeof(double));
return arr;
@@ -990,11 +990,11 @@ NT_String* NT_GetValueStringArray(const struct NT_Value* value,
*last_change = value->last_change;
*arr_size = value->data.arr_string.size;
NT_String* arr = static_cast<NT_String*>(
wpi::CheckedMalloc(value->data.arr_string.size * sizeof(NT_String)));
wpi::safe_malloc(value->data.arr_string.size * sizeof(NT_String)));
for (size_t i = 0; i < value->data.arr_string.size; ++i) {
size_t len = value->data.arr_string.arr[i].len;
arr[i].len = len;
arr[i].str = static_cast<char*>(wpi::CheckedMalloc(len + 1));
arr[i].str = static_cast<char*>(wpi::safe_malloc(len + 1));
std::memcpy(arr[i].str, value->data.arr_string.arr[i].str, len + 1);
}
return arr;
@@ -1099,7 +1099,7 @@ NT_Bool* NT_GetEntryBooleanArray(NT_Entry entry, uint64_t* last_change,
*last_change = v->last_change();
auto vArr = v->GetBooleanArray();
NT_Bool* arr =
static_cast<int*>(wpi::CheckedMalloc(vArr.size() * sizeof(NT_Bool)));
static_cast<int*>(wpi::safe_malloc(vArr.size() * sizeof(NT_Bool)));
*arr_size = vArr.size();
std::copy(vArr.begin(), vArr.end(), arr);
return arr;
@@ -1112,7 +1112,7 @@ double* NT_GetEntryDoubleArray(NT_Entry entry, uint64_t* last_change,
*last_change = v->last_change();
auto vArr = v->GetDoubleArray();
double* arr =
static_cast<double*>(wpi::CheckedMalloc(vArr.size() * sizeof(double)));
static_cast<double*>(wpi::safe_malloc(vArr.size() * sizeof(double)));
*arr_size = vArr.size();
std::copy(vArr.begin(), vArr.end(), arr);
return arr;
@@ -1125,7 +1125,7 @@ NT_String* NT_GetEntryStringArray(NT_Entry entry, uint64_t* last_change,
*last_change = v->last_change();
auto vArr = v->GetStringArray();
NT_String* arr = static_cast<NT_String*>(
wpi::CheckedMalloc(vArr.size() * sizeof(NT_String)));
wpi::safe_malloc(vArr.size() * sizeof(NT_String)));
for (size_t i = 0; i < vArr.size(); ++i) {
ConvertToC(vArr[i], &arr[i]);
}

View File

@@ -7,14 +7,14 @@
#include "ntcore_test.h"
#include <wpi/memory.h>
#include <wpi/MemAlloc.h>
#include "Value_internal.h"
extern "C" {
struct NT_String* NT_GetStringForTesting(const char* string, int* struct_size) {
struct NT_String* str =
static_cast<NT_String*>(wpi::CheckedCalloc(1, sizeof(NT_String)));
static_cast<NT_String*>(wpi::safe_calloc(1, sizeof(NT_String)));
nt::ConvertToC(wpi::StringRef(string), str);
*struct_size = sizeof(NT_String);
return str;
@@ -26,7 +26,7 @@ struct NT_EntryInfo* NT_GetEntryInfoForTesting(const char* name,
uint64_t last_change,
int* struct_size) {
struct NT_EntryInfo* entry_info =
static_cast<NT_EntryInfo*>(wpi::CheckedCalloc(1, sizeof(NT_EntryInfo)));
static_cast<NT_EntryInfo*>(wpi::safe_calloc(1, sizeof(NT_EntryInfo)));
nt::ConvertToC(wpi::StringRef(name), &entry_info->name);
entry_info->type = type;
entry_info->flags = flags;
@@ -44,7 +44,7 @@ struct NT_ConnectionInfo* NT_GetConnectionInfoForTesting(
const char* remote_id, const char* remote_ip, unsigned int remote_port,
uint64_t last_update, unsigned int protocol_version, int* struct_size) {
struct NT_ConnectionInfo* conn_info = static_cast<NT_ConnectionInfo*>(
wpi::CheckedCalloc(1, sizeof(NT_ConnectionInfo)));
wpi::safe_calloc(1, sizeof(NT_ConnectionInfo)));
nt::ConvertToC(wpi::StringRef(remote_id), &conn_info->remote_id);
nt::ConvertToC(wpi::StringRef(remote_ip), &conn_info->remote_ip);
conn_info->remote_port = remote_port;
@@ -63,7 +63,7 @@ void NT_FreeConnectionInfoForTesting(struct NT_ConnectionInfo* info) {
struct NT_Value* NT_GetValueBooleanForTesting(uint64_t last_change, int val,
int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::CheckedCalloc(1, sizeof(NT_Value)));
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_BOOLEAN;
value->last_change = last_change;
value->data.v_boolean = val;
@@ -74,7 +74,7 @@ struct NT_Value* NT_GetValueBooleanForTesting(uint64_t last_change, int val,
struct NT_Value* NT_GetValueDoubleForTesting(uint64_t last_change, double val,
int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::CheckedCalloc(1, sizeof(NT_Value)));
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_DOUBLE;
value->last_change = last_change;
value->data.v_double = val;
@@ -86,7 +86,7 @@ struct NT_Value* NT_GetValueStringForTesting(uint64_t last_change,
const char* str,
int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::CheckedCalloc(1, sizeof(NT_Value)));
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_STRING;
value->last_change = last_change;
nt::ConvertToC(wpi::StringRef(str), &value->data.v_string);
@@ -97,7 +97,7 @@ struct NT_Value* NT_GetValueStringForTesting(uint64_t last_change,
struct NT_Value* NT_GetValueRawForTesting(uint64_t last_change, const char* raw,
int raw_len, int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::CheckedCalloc(1, sizeof(NT_Value)));
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_RAW;
value->last_change = last_change;
nt::ConvertToC(wpi::StringRef(raw, raw_len), &value->data.v_string);
@@ -110,7 +110,7 @@ struct NT_Value* NT_GetValueBooleanArrayForTesting(uint64_t last_change,
size_t array_len,
int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::CheckedCalloc(1, sizeof(NT_Value)));
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_BOOLEAN_ARRAY;
value->last_change = last_change;
value->data.arr_boolean.arr = NT_AllocateBooleanArray(array_len);
@@ -126,7 +126,7 @@ struct NT_Value* NT_GetValueDoubleArrayForTesting(uint64_t last_change,
size_t array_len,
int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::CheckedCalloc(1, sizeof(NT_Value)));
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_BOOLEAN;
value->last_change = last_change;
value->data.arr_double.arr = NT_AllocateDoubleArray(array_len);
@@ -142,7 +142,7 @@ struct NT_Value* NT_GetValueStringArrayForTesting(uint64_t last_change,
size_t array_len,
int* struct_size) {
struct NT_Value* value =
static_cast<NT_Value*>(wpi::CheckedCalloc(1, sizeof(NT_Value)));
static_cast<NT_Value*>(wpi::safe_calloc(1, sizeof(NT_Value)));
value->type = NT_BOOLEAN;
value->last_change = last_change;
value->data.arr_string.arr = NT_AllocateStringArray(array_len);
@@ -151,7 +151,7 @@ struct NT_Value* NT_GetValueStringArrayForTesting(uint64_t last_change,
size_t len = arr[i].len;
value->data.arr_string.arr[i].len = len;
value->data.arr_string.arr[i].str =
static_cast<char*>(wpi::CheckedMalloc(len + 1));
static_cast<char*>(wpi::safe_malloc(len + 1));
std::memcpy(value->data.arr_string.arr[i].str, arr[i].str, len + 1);
}
*struct_size = sizeof(NT_Value);
@@ -173,8 +173,8 @@ static void CopyNtString(const struct NT_String* copy_from,
struct NT_RpcParamDef* NT_GetRpcParamDefForTesting(const char* name,
const struct NT_Value* val,
int* struct_size) {
struct NT_RpcParamDef* def = static_cast<NT_RpcParamDef*>(
wpi::CheckedCalloc(1, sizeof(NT_RpcParamDef)));
struct NT_RpcParamDef* def =
static_cast<NT_RpcParamDef*>(wpi::safe_calloc(1, sizeof(NT_RpcParamDef)));
nt::ConvertToC(wpi::StringRef(name), &def->name);
CopyNtValue(val, &def->def_value);
*struct_size = sizeof(NT_RpcParamDef);
@@ -191,7 +191,7 @@ struct NT_RpcResultDef* NT_GetRpcResultsDefForTesting(const char* name,
enum NT_Type type,
int* struct_size) {
struct NT_RpcResultDef* def = static_cast<NT_RpcResultDef*>(
wpi::CheckedCalloc(1, sizeof(NT_RpcResultDef)));
wpi::safe_calloc(1, sizeof(NT_RpcResultDef)));
nt::ConvertToC(wpi::StringRef(name), &def->name);
def->type = type;
*struct_size = sizeof(NT_RpcResultDef);
@@ -208,19 +208,19 @@ struct NT_RpcDefinition* NT_GetRpcDefinitionForTesting(
const struct NT_RpcParamDef* params, size_t num_results,
const struct NT_RpcResultDef* results, int* struct_size) {
struct NT_RpcDefinition* def = static_cast<NT_RpcDefinition*>(
wpi::CheckedCalloc(1, sizeof(NT_RpcDefinition)));
wpi::safe_calloc(1, sizeof(NT_RpcDefinition)));
def->version = version;
nt::ConvertToC(wpi::StringRef(name), &def->name);
def->num_params = num_params;
def->params = static_cast<NT_RpcParamDef*>(
wpi::CheckedMalloc(num_params * sizeof(NT_RpcParamDef)));
wpi::safe_malloc(num_params * sizeof(NT_RpcParamDef)));
for (size_t i = 0; i < num_params; ++i) {
CopyNtString(&params[i].name, &def->params[i].name);
CopyNtValue(&params[i].def_value, &def->params[i].def_value);
}
def->num_results = num_results;
def->results = static_cast<NT_RpcResultDef*>(
wpi::CheckedMalloc(num_results * sizeof(NT_RpcResultDef)));
wpi::safe_malloc(num_results * sizeof(NT_RpcResultDef)));
for (size_t i = 0; i < num_results; ++i) {
CopyNtString(&results[i].name, &def->results[i].name);
def->results[i].type = results[i].type;
@@ -234,7 +234,7 @@ struct NT_RpcAnswer* NT_GetRpcAnswerForTesting(
unsigned int rpc_id, unsigned int call_uid, const char* name,
const char* params, size_t params_len, int* struct_size) {
struct NT_RpcAnswer* info =
static_cast<NT_RpcAnswer*>(wpi::CheckedCalloc(1, sizeof(NT_RpcAnswer)));
static_cast<NT_RpcAnswer*>(wpi::safe_calloc(1, sizeof(NT_RpcAnswer)));
info->entry = rpc_id;
info->call = call_uid;
nt::ConvertToC(wpi::StringRef(name), &info->name);

View File

@@ -13,18 +13,26 @@ generatedFileExclude {
src/main/native/include/llvm/
src/main/native/include/wpi/AlignOf\.h$
src/main/native/include/wpi/ArrayRef\.h$
src/main/native/include/wpi/Chrono\.h$
src/main/native/include/wpi/Compiler\.h$
src/main/native/include/wpi/ConvertUTF\.h$
src/main/native/include/wpi/DenseMap\.h$
src/main/native/include/wpi/DenseMapInfo\.h$
src/main/native/include/wpi/EpochTracker\.h$
src/main/native/include/wpi/Endian\.h$
src/main/native/include/wpi/Errc\.h$
src/main/native/include/wpi/Errno\.h$
src/main/native/include/wpi/Error\.h$
src/main/native/include/wpi/ErrorHandling\.h$
src/main/native/include/wpi/ErrorOr\.h$
src/main/native/include/wpi/FileSystem\.h$
src/main/native/include/wpi/Format\.h$
src/main/native/include/wpi/Hashing\.h$
src/main/native/include/wpi/IntrusiveRefCntPtr\.h$
src/main/native/include/wpi/ManagedStatic\.h$
src/main/native/include/wpi/MapVector\.h$
src/main/native/include/wpi/MathExtras\.h$
src/main/native/include/wpi/MemAlloc\.h$
src/main/native/include/wpi/NativeFormatting\.h$
src/main/native/include/wpi/Path\.h$
src/main/native/include/wpi/PointerLikeTypeTraits\.h$
@@ -37,7 +45,9 @@ generatedFileExclude {
src/main/native/include/wpi/StringExtras\.h$
src/main/native/include/wpi/StringMap\.h$
src/main/native/include/wpi/StringRef\.h$
src/main/native/include/wpi/SwapByteOrder\.h$
src/main/native/include/wpi/Twine\.h$
src/main/native/include/wpi/VersionTuple\.h$
src/main/native/include/wpi/WindowsError\.h$
src/main/native/include/wpi/http_parser\.h$
src/main/native/include/wpi/iterator\.h$

View File

@@ -8,9 +8,9 @@
*===------------------------------------------------------------------------=*/
/*
* Copyright 2001-2004 Unicode, Inc.
*
*
* Disclaimer
*
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
@@ -18,9 +18,9 @@
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
*
* Limitations on Rights to Redistribute This Code
*
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
@@ -117,7 +117,7 @@ static const char trailingBytesForUTF8[256] = {
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
@@ -143,7 +143,7 @@ static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF16 (
const UTF32** sourceStart, const UTF32* sourceEnd,
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
@@ -192,7 +192,7 @@ ConversionResult ConvertUTF32toUTF16 (
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF32 (
const UTF16** sourceStart, const UTF16* sourceEnd,
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
@@ -246,7 +246,7 @@ if (result == sourceIllegal) {
return result;
}
ConversionResult ConvertUTF16toUTF8 (
const UTF16** sourceStart, const UTF16* sourceEnd,
const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
@@ -255,7 +255,7 @@ ConversionResult ConvertUTF16toUTF8 (
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF32 byteMark = 0x80;
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
@@ -316,7 +316,7 @@ ConversionResult ConvertUTF16toUTF8 (
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF8 (
const UTF32** sourceStart, const UTF32* sourceEnd,
const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
@@ -325,7 +325,7 @@ ConversionResult ConvertUTF32toUTF8 (
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF32 byteMark = 0x80;
ch = *source++;
if (flags == strictConversion ) {
/* UTF-16 surrogate values are illegal in UTF-32 */
@@ -347,7 +347,7 @@ ConversionResult ConvertUTF32toUTF8 (
ch = UNI_REPLACEMENT_CHAR;
result = sourceIllegal;
}
target += bytesToWrite;
if (target > targetEnd) {
--source; /* Back up source pointer! */
@@ -540,7 +540,7 @@ Boolean isLegalUTF8String(const UTF8 **source, const UTF8 *sourceEnd) {
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16 (
const UTF8** sourceStart, const UTF8* sourceEnd,
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
@@ -613,7 +613,7 @@ ConversionResult ConvertUTF8toUTF16 (
/* --------------------------------------------------------------------- */
static ConversionResult ConvertUTF8toUTF32Impl(
const UTF8** sourceStart, const UTF8* sourceEnd,
const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags,
Boolean InputIsPartial) {
ConversionResult result = conversionOK;

View File

@@ -0,0 +1,138 @@
//===----- lib/Support/Error.cpp - Error and associated utilities ---------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "wpi/Error.h"
#include "wpi/Twine.h"
#include "wpi/ErrorHandling.h"
#include "wpi/ManagedStatic.h"
#include <system_error>
using namespace wpi;
namespace {
enum class ErrorErrorCode : int {
MultipleErrors = 1,
FileError,
InconvertibleError
};
// FIXME: This class is only here to support the transition to wpi::Error. It
// will be removed once this transition is complete. Clients should prefer to
// deal with the Error value directly, rather than converting to error_code.
class ErrorErrorCategory : public std::error_category {
public:
const char *name() const noexcept override { return "Error"; }
std::string message(int condition) const override {
switch (static_cast<ErrorErrorCode>(condition)) {
case ErrorErrorCode::MultipleErrors:
return "Multiple errors";
case ErrorErrorCode::InconvertibleError:
return "Inconvertible error value. An error has occurred that could "
"not be converted to a known std::error_code. Please file a "
"bug.";
case ErrorErrorCode::FileError:
return "A file error occurred.";
}
wpi_unreachable("Unhandled error code");
}
};
}
static ManagedStatic<ErrorErrorCategory> ErrorErrorCat;
namespace wpi {
void ErrorInfoBase::anchor() {}
char ErrorInfoBase::ID = 0;
char ErrorList::ID = 0;
void ECError::anchor() {}
char ECError::ID = 0;
char StringError::ID = 0;
char FileError::ID = 0;
void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) {
if (!E)
return;
OS << ErrorBanner;
handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {
EI.log(OS);
OS << "\n";
});
}
std::error_code ErrorList::convertToErrorCode() const {
return std::error_code(static_cast<int>(ErrorErrorCode::MultipleErrors),
*ErrorErrorCat);
}
std::error_code inconvertibleErrorCode() {
return std::error_code(static_cast<int>(ErrorErrorCode::InconvertibleError),
*ErrorErrorCat);
}
std::error_code FileError::convertToErrorCode() const {
return std::error_code(static_cast<int>(ErrorErrorCode::FileError),
*ErrorErrorCat);
}
Error errorCodeToError(std::error_code EC) {
if (!EC)
return Error::success();
return Error(wpi::make_unique<ECError>(ECError(EC)));
}
std::error_code errorToErrorCode(Error Err) {
std::error_code EC;
handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) {
EC = EI.convertToErrorCode();
});
if (EC == inconvertibleErrorCode())
report_fatal_error(EC.message());
return EC;
}
StringError::StringError(std::error_code EC, const Twine &S)
: Msg(S.str()), EC(EC) {}
StringError::StringError(const Twine &S, std::error_code EC)
: Msg(S.str()), EC(EC), PrintMsgOnly(true) {}
void StringError::log(raw_ostream &OS) const {
if (PrintMsgOnly) {
OS << Msg;
} else {
OS << EC.message();
if (!Msg.empty())
OS << (" " + Msg);
}
}
std::error_code StringError::convertToErrorCode() const {
return EC;
}
Error createStringError(std::error_code EC, char const *Msg) {
return make_error<StringError>(Msg, EC);
}
void report_fatal_error(Error Err, bool GenCrashDiag) {
assert(Err && "report_fatal_error called with success value");
std::string ErrMsg;
{
raw_string_ostream ErrStream(ErrMsg);
logAllUnhandledErrors(std::move(Err), ErrStream);
}
report_fatal_error(ErrMsg);
}
} // end namespace wpi

View File

@@ -12,7 +12,191 @@
//
//===----------------------------------------------------------------------===//
#include "wpi/ErrorHandling.h"
#include "wpi/SmallVector.h"
#include "wpi/Twine.h"
#include "wpi/Error.h"
#include "wpi/WindowsError.h"
#include "wpi/raw_ostream.h"
#include <cassert>
#include <cstdlib>
#include <mutex>
#include <new>
#ifndef _WIN32
#include <unistd.h>
#endif
#if defined(_MSC_VER)
#include <io.h>
#endif
using namespace wpi;
static fatal_error_handler_t ErrorHandler = nullptr;
static void *ErrorHandlerUserData = nullptr;
static fatal_error_handler_t BadAllocErrorHandler = nullptr;
static void *BadAllocErrorHandlerUserData = nullptr;
// Mutexes to synchronize installing error handlers and calling error handlers.
// Do not use ManagedStatic, or that may allocate memory while attempting to
// report an OOM.
//
// This usage of std::mutex has to be conditionalized behind ifdefs because
// of this script:
// compiler-rt/lib/sanitizer_common/symbolizer/scripts/build_symbolizer.sh
// That script attempts to statically link the LLVM symbolizer library with the
// STL and hide all of its symbols with 'opt -internalize'. To reduce size, it
// cuts out the threading portions of the hermetic copy of libc++ that it
// builds. We can remove these ifdefs if that script goes away.
static std::mutex ErrorHandlerMutex;
static std::mutex BadAllocErrorHandlerMutex;
void wpi::install_fatal_error_handler(fatal_error_handler_t handler,
void *user_data) {
std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
assert(!ErrorHandler && "Error handler already registered!\n");
ErrorHandler = handler;
ErrorHandlerUserData = user_data;
}
void wpi::remove_fatal_error_handler() {
std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
ErrorHandler = nullptr;
ErrorHandlerUserData = nullptr;
}
void wpi::report_fatal_error(const char *Reason, bool GenCrashDiag) {
report_fatal_error(Twine(Reason), GenCrashDiag);
}
void wpi::report_fatal_error(const std::string &Reason, bool GenCrashDiag) {
report_fatal_error(Twine(Reason), GenCrashDiag);
}
void wpi::report_fatal_error(StringRef Reason, bool GenCrashDiag) {
report_fatal_error(Twine(Reason), GenCrashDiag);
}
void wpi::report_fatal_error(const Twine &Reason, bool GenCrashDiag) {
wpi::fatal_error_handler_t handler = nullptr;
void* handlerData = nullptr;
{
// Only acquire the mutex while reading the handler, so as not to invoke a
// user-supplied callback under a lock.
std::lock_guard<std::mutex> Lock(ErrorHandlerMutex);
handler = ErrorHandler;
handlerData = ErrorHandlerUserData;
}
if (handler) {
handler(handlerData, Reason.str(), GenCrashDiag);
} else {
// Blast the result out to stderr. We don't try hard to make sure this
// succeeds (e.g. handling EINTR) and we can't use errs() here because
// raw ostreams can call report_fatal_error.
SmallVector<char, 64> Buffer;
raw_svector_ostream OS(Buffer);
OS << "LLVM ERROR: " << Reason << "\n";
StringRef MessageStr = OS.str();
#ifdef _WIN32
int written = ::_write(2, MessageStr.data(), MessageStr.size());
#else
ssize_t written = ::write(2, MessageStr.data(), MessageStr.size());
#endif
(void)written; // If something went wrong, we deliberately just give up.
}
exit(1);
}
void wpi::install_bad_alloc_error_handler(fatal_error_handler_t handler,
void *user_data) {
std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
assert(!ErrorHandler && "Bad alloc error handler already registered!\n");
BadAllocErrorHandler = handler;
BadAllocErrorHandlerUserData = user_data;
}
void wpi::remove_bad_alloc_error_handler() {
std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
BadAllocErrorHandler = nullptr;
BadAllocErrorHandlerUserData = nullptr;
}
void wpi::report_bad_alloc_error(const char *Reason, bool GenCrashDiag) {
fatal_error_handler_t Handler = nullptr;
void *HandlerData = nullptr;
{
// Only acquire the mutex while reading the handler, so as not to invoke a
// user-supplied callback under a lock.
std::lock_guard<std::mutex> Lock(BadAllocErrorHandlerMutex);
Handler = BadAllocErrorHandler;
HandlerData = BadAllocErrorHandlerUserData;
}
if (Handler) {
Handler(HandlerData, Reason, GenCrashDiag);
wpi_unreachable("bad alloc handler should not return");
}
// Don't call the normal error handler. It may allocate memory. Directly write
// an OOM to stderr and abort.
char OOMMessage[] = "LLVM ERROR: out of memory\n";
#ifdef _WIN32
int written = ::_write(2, OOMMessage, strlen(OOMMessage));
#else
ssize_t written = ::write(2, OOMMessage, strlen(OOMMessage));
#endif
(void)written;
abort();
}
// Causes crash on allocation failure. It is called prior to the handler set by
// 'install_bad_alloc_error_handler'.
static void out_of_memory_new_handler() {
wpi::report_bad_alloc_error("Allocation failed");
}
// Installs new handler that causes crash on allocation failure. It does not
// need to be called explicitly, if this file is linked to application, because
// in this case it is called during construction of 'new_handler_installer'.
void wpi::install_out_of_memory_new_handler() {
static bool out_of_memory_new_handler_installed = false;
if (!out_of_memory_new_handler_installed) {
std::set_new_handler(out_of_memory_new_handler);
out_of_memory_new_handler_installed = true;
}
}
// Static object that causes installation of 'out_of_memory_new_handler' before
// execution of 'main'.
static class NewHandlerInstaller {
public:
NewHandlerInstaller() {
install_out_of_memory_new_handler();
}
} new_handler_installer;
void wpi::wpi_unreachable_internal(const char *msg, const char *file,
unsigned line) {
// This code intentionally doesn't call the ErrorHandler callback, because
// wpi_unreachable is intended to be used to indicate "impossible"
// situations, and not legitimate runtime errors.
if (msg)
errs() << msg << "\n";
errs() << "UNREACHABLE executed";
if (file)
errs() << " at " << file << ":" << line;
errs() << "!\n";
abort();
#ifdef LLVM_BUILTIN_UNREACHABLE
// Windows systems and possibly others don't declare abort() to be noreturn,
// so use the unreachable builtin to avoid a Clang self-host warning.
LLVM_BUILTIN_UNREACHABLE;
#endif
}
#ifdef _WIN32

View File

@@ -20,10 +20,10 @@ using namespace wpi;
// Provide a definition and static initializer for the fixed seed. This
// initializer should always be zero to ensure its value can never appear to be
// non-zero, even during dynamic initialization.
size_t wpi::hashing::detail::fixed_seed_override = 0;
uint64_t wpi::hashing::detail::fixed_seed_override = 0;
// Implement the function for forced setting of the fixed seed.
// FIXME: Use atomic operations here so that there is no data race.
void wpi::set_fixed_execution_hash_seed(size_t fixed_value) {
void wpi::set_fixed_execution_hash_seed(uint64_t fixed_value) {
hashing::detail::fixed_seed_override = fixed_value;
}

View File

@@ -0,0 +1,72 @@
//===-- ManagedStatic.cpp - Static Global wrapper -------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the ManagedStatic class and wpi_shutdown().
//
//===----------------------------------------------------------------------===//
#include "wpi/ManagedStatic.h"
#include "wpi/mutex.h"
#include <cassert>
#include <mutex>
using namespace wpi;
static const ManagedStaticBase *StaticList = nullptr;
static wpi::mutex *ManagedStaticMutex = nullptr;
static std::once_flag mutex_init_flag;
static void initializeMutex() {
ManagedStaticMutex = new wpi::mutex();
}
static wpi::mutex* getManagedStaticMutex() {
std::call_once(mutex_init_flag, initializeMutex);
return ManagedStaticMutex;
}
void ManagedStaticBase::RegisterManagedStatic(void *(*Creator)(),
void (*Deleter)(void*)) const {
assert(Creator);
std::lock_guard<wpi::mutex> Lock(*getManagedStaticMutex());
if (!Ptr.load(std::memory_order_relaxed)) {
void *Tmp = Creator();
Ptr.store(Tmp, std::memory_order_release);
DeleterFn = Deleter;
// Add to list of managed statics.
Next = StaticList;
StaticList = this;
}
}
void ManagedStaticBase::destroy() const {
assert(DeleterFn && "ManagedStatic not initialized correctly!");
assert(StaticList == this &&
"Not destroyed in reverse order of construction?");
// Unlink from list.
StaticList = Next;
Next = nullptr;
// Destroy memory.
DeleterFn(Ptr);
// Cleanup.
Ptr = nullptr;
DeleterFn = nullptr;
}
/// wpi_shutdown - Deallocate and destroy all ManagedStatic variables.
void wpi::wpi_shutdown() {
std::lock_guard<wpi::mutex> Lock(*getManagedStaticMutex());
while (StaticList)
StaticList->destroy();
}

View File

@@ -12,7 +12,12 @@
//===----------------------------------------------------------------------===//
#include "wpi/Path.h"
#include "wpi/ArrayRef.h"
#include "wpi/Endian.h"
#include "wpi/Errc.h"
#include "wpi/ErrorHandling.h"
#include "wpi/FileSystem.h"
#include "wpi/SmallString.h"
#include <cctype>
#include <cstring>
@@ -22,10 +27,8 @@
#include <io.h>
#endif
#include "wpi/FileSystem.h"
#include "wpi/SmallString.h"
using namespace wpi;
using namespace wpi::support::endian;
namespace {
using wpi::StringRef;
@@ -444,7 +447,7 @@ void replace_path_prefix(SmallVectorImpl<char> &Path,
// If prefixes have the same size we can simply copy the new one over.
if (OldPrefix.size() == NewPrefix.size()) {
std::copy(NewPrefix.begin(), NewPrefix.end(), Path.begin());
wpi::copy(NewPrefix, Path.begin());
return;
}
@@ -674,9 +677,8 @@ std::error_code getUniqueID(const Twine Path, UniqueID &Result) {
return std::error_code();
}
static std::error_code make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path,
bool use_current_directory) {
void make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path) {
StringRef p(path.data(), path.size());
bool rootDirectory = path::has_root_directory(p);
@@ -685,14 +687,11 @@ static std::error_code make_absolute(const Twine &current_directory,
// Already absolute.
if (rootName && rootDirectory)
return std::error_code();
return;
// All of the following conditions will need the current directory.
SmallString<128> current_dir;
if (use_current_directory)
current_directory.toVector(current_dir);
else if (std::error_code ec = current_path(current_dir))
return ec;
current_directory.toVector(current_dir);
// Relative path. Prepend the current directory.
if (!rootName && !rootDirectory) {
@@ -700,7 +699,7 @@ static std::error_code make_absolute(const Twine &current_directory,
path::append(current_dir, p);
// Set path to the result.
path.swap(current_dir);
return std::error_code();
return;
}
if (!rootName && rootDirectory) {
@@ -709,7 +708,7 @@ static std::error_code make_absolute(const Twine &current_directory,
path::append(curDirRootName, p);
// Set path to the result.
path.swap(curDirRootName);
return std::error_code();
return;
}
if (rootName && !rootDirectory) {
@@ -721,21 +720,23 @@ static std::error_code make_absolute(const Twine &current_directory,
SmallString<128> res;
path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath);
path.swap(res);
return std::error_code();
return;
}
assert(false && "All rootName and rootDirectory combinations should have "
wpi_unreachable("All rootName and rootDirectory combinations should have "
"occurred above!");
return std::error_code();
}
std::error_code make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path) {
return make_absolute(current_directory, path, true);
}
std::error_code make_absolute(SmallVectorImpl<char> &path) {
return make_absolute(Twine(), path, false);
if (path::is_absolute(path))
return {};
SmallString<128> current_dir;
if (std::error_code ec = current_path(current_dir))
return ec;
make_absolute(current_dir, path);
return {};
}
bool exists(const basic_file_status &status) {
@@ -803,12 +804,13 @@ std::error_code is_other(const Twine &Path, bool &Result) {
return std::error_code();
}
void directory_entry::replace_filename(const Twine &filename,
basic_file_status st) {
SmallString<128> path = path::parent_path(Path);
path::append(path, filename);
Path = path.str();
Status = st;
void directory_entry::replace_filename(const Twine &Filename, file_type Type,
basic_file_status Status) {
SmallString<128> PathStr = path::parent_path(Path);
path::append(PathStr, Filename);
this->Path = PathStr.str();
this->Type = Type;
this->Status = Status;
}
ErrorOr<perms> getPermissions(const Twine &Path) {
@@ -829,20 +831,3 @@ ErrorOr<perms> getPermissions(const Twine &Path) {
#else
#include "Unix/Path.inc"
#endif
namespace wpi {
namespace sys {
namespace path {
bool user_cache_directory(SmallVectorImpl<char> &Result, const Twine &Path1,
const Twine &Path2, const Twine &Path3) {
if (getUserCacheDir(Result)) {
append(Result, Path1, Path2, Path3);
return true;
}
return false;
}
} // end namespace path
} // end namsspace sys
} // end namespace wpi

View File

@@ -15,7 +15,7 @@
#include "wpi/SmallPtrSet.h"
#include "wpi/DenseMapInfo.h"
#include "wpi/MathExtras.h"
#include "wpi/memory.h"
#include "wpi/ErrorHandling.h"
#include <algorithm>
#include <cassert>
#include <cstdlib>
@@ -32,7 +32,8 @@ void SmallPtrSetImplBase::shrink_and_clear() {
NumNonEmpty = NumTombstones = 0;
// Install the new array. Clear all the buckets to empty.
CurArray = (const void**)CheckedMalloc(sizeof(void*) * CurArraySize);
CurArray = (const void**)safe_malloc(sizeof(void*) * CurArraySize);
memset(CurArray, -1, CurArraySize*sizeof(void*));
}
@@ -96,7 +97,10 @@ void SmallPtrSetImplBase::Grow(unsigned NewSize) {
bool WasSmall = isSmall();
// Install the new array. Clear all the buckets to empty.
CurArray = (const void**) CheckedMalloc(sizeof(void*) * NewSize);
const void **NewBuckets = (const void**) safe_malloc(sizeof(void*) * NewSize);
// Reset member only if memory was allocated successfully
CurArray = NewBuckets;
CurArraySize = NewSize;
memset(CurArray, -1, NewSize*sizeof(void*));
@@ -123,7 +127,7 @@ SmallPtrSetImplBase::SmallPtrSetImplBase(const void **SmallStorage,
CurArray = SmallArray;
// Otherwise, allocate new heap space (unless we were the same size)
} else {
CurArray = (const void**)CheckedMalloc(sizeof(void*) * that.CurArraySize);
CurArray = (const void**)safe_malloc(sizeof(void*) * that.CurArraySize);
}
// Copy over the that array.
@@ -152,15 +156,12 @@ void SmallPtrSetImplBase::CopyFrom(const SmallPtrSetImplBase &RHS) {
// Otherwise, allocate new heap space (unless we were the same size)
} else if (CurArraySize != RHS.CurArraySize) {
if (isSmall())
CurArray = (const void**)malloc(sizeof(void*) * RHS.CurArraySize);
CurArray = (const void**)safe_malloc(sizeof(void*) * RHS.CurArraySize);
else {
const void **T = (const void**)realloc(CurArray,
const void **T = (const void**)safe_realloc(CurArray,
sizeof(void*) * RHS.CurArraySize);
if (!T)
free(CurArray);
CurArray = T;
}
assert(CurArray && "Failed to allocate memory?");
}
CopyHelper(RHS);

View File

@@ -12,30 +12,32 @@
//===----------------------------------------------------------------------===//
#include "wpi/SmallVector.h"
#include "wpi/memory.h"
#include "wpi/MemAlloc.h"
using namespace wpi;
/// grow_pod - This is an implementation of the grow() method which only works
/// on POD-like datatypes and is out of line to reduce code duplication.
void SmallVectorBase::grow_pod(void *FirstEl, size_t MinSizeInBytes,
void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity,
size_t TSize) {
size_t CurSizeBytes = size_in_bytes();
size_t NewCapacityInBytes = 2 * capacity_in_bytes() + TSize; // Always grow.
if (NewCapacityInBytes < MinSizeInBytes)
NewCapacityInBytes = MinSizeInBytes;
// Ensure we can fit the new capacity in 32 bits.
if (MinCapacity > UINT32_MAX)
report_bad_alloc_error("SmallVector capacity overflow during allocation");
size_t NewCapacity = 2 * capacity() + 1; // Always grow.
NewCapacity =
std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX));
void *NewElts;
if (BeginX == FirstEl) {
NewElts = CheckedMalloc(NewCapacityInBytes);
NewElts = safe_malloc(NewCapacity * TSize);
// Copy the elements over. No need to run dtors on PODs.
memcpy(NewElts, this->BeginX, CurSizeBytes);
memcpy(NewElts, this->BeginX, size() * TSize);
} else {
// If this wasn't grown from the inline copy, grow the allocated space.
NewElts = CheckedRealloc(this->BeginX, NewCapacityInBytes);
NewElts = safe_realloc(this->BeginX, NewCapacity * TSize);
}
this->EndX = (char*)NewElts+CurSizeBytes;
this->BeginX = NewElts;
this->CapacityX = (char*)this->BeginX + NewCapacityInBytes;
this->Capacity = NewCapacity;
}

View File

@@ -58,16 +58,33 @@ void wpi::SplitString(StringRef Source,
}
}
void wpi::PrintEscapedString(StringRef Name, raw_ostream &Out) {
void wpi::printEscapedString(StringRef Name, raw_ostream &Out) {
for (unsigned i = 0, e = Name.size(); i != e; ++i) {
unsigned char C = Name[i];
if (isprint(C) && C != '\\' && C != '"')
if (isPrint(C) && C != '\\' && C != '"')
Out << C;
else
Out << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
}
}
void wpi::printHTMLEscaped(StringRef String, raw_ostream &Out) {
for (char C : String) {
if (C == '&')
Out << "&amp;";
else if (C == '<')
Out << "&lt;";
else if (C == '>')
Out << "&gt;";
else if (C == '\"')
Out << "&quot;";
else if (C == '\'')
Out << "&apos;";
else
Out << C;
}
}
void wpi::printLowerCase(StringRef String, raw_ostream &Out) {
for (const char C : String)
Out << toLower(C);

View File

@@ -15,7 +15,6 @@
#include "wpi/StringExtras.h"
#include "wpi/Compiler.h"
#include "wpi/MathExtras.h"
#include "wpi/memory.h"
#include <cassert>
using namespace wpi;
@@ -59,7 +58,7 @@ void StringMapImpl::init(unsigned InitSize) {
NumTombstones = 0;
TheTable = static_cast<StringMapEntryBase **>(
CheckedCalloc(NewNumBuckets+1,
safe_calloc(NewNumBuckets+1,
sizeof(StringMapEntryBase **) + sizeof(unsigned)));
// Set the member only if TheTable was successfully allocated
@@ -129,7 +128,6 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
}
}
/// FindKey - Look up the bucket that contains the specified key. If it exists
/// in the map, return the bucket number of the key. Otherwise return -1.
/// This does not modify the map.
@@ -219,7 +217,7 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) {
// Allocate one extra bucket which will always be non-empty. This allows the
// iterators to stop at end.
auto NewTableArray = static_cast<StringMapEntryBase **>(
CheckedCalloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned)));
safe_calloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned)));
unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1);
NewTableArray[NewSize] = (StringMapEntryBase*)2;

View File

@@ -16,25 +16,45 @@
//=== is guaranteed to work on *all* UNIX variants.
//===----------------------------------------------------------------------===//
#include "wpi/Errno.h"
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <dirent.h>
#define NAMLEN(dirent) strlen((dirent)->d_name)
#include <pwd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <unistd.h>
using namespace wpi;
namespace wpi {
namespace sys {
namespace fs {
const file_t kInvalidFile = -1;
TimePoint<> basic_file_status::getLastAccessedTime() const {
return toTimePoint(fs_st_atime, fs_st_atime_nsec);
}
TimePoint<> basic_file_status::getLastModificationTime() const {
return toTimePoint(fs_st_mtime, fs_st_mtime_nsec);
}
UniqueID file_status::getUniqueID() const {
return UniqueID(fs_st_dev, fs_st_ino);
}
uint32_t file_status::getLinkCount() const {
return fs_st_nlinks;
}
std::error_code current_path(SmallVectorImpl<char> &result) {
result.clear();
@@ -93,9 +113,9 @@ std::error_code access(const Twine &Path, AccessMode Mode) {
// Don't say that directories are executable.
struct stat buf;
if (0 != stat(P.begin(), &buf))
return std::make_error_code(std::errc::permission_denied);
return errc::permission_denied;
if (!S_ISREG(buf.st_mode))
return std::make_error_code(std::errc::permission_denied);
return errc::permission_denied;
}
return std::error_code();
@@ -117,37 +137,48 @@ std::error_code equivalent(const Twine &A, const Twine &B, bool &result) {
return std::error_code();
}
static file_type typeForMode(mode_t Mode) {
if (S_ISDIR(Mode))
return file_type::directory_file;
else if (S_ISREG(Mode))
return file_type::regular_file;
else if (S_ISBLK(Mode))
return file_type::block_file;
else if (S_ISCHR(Mode))
return file_type::character_file;
else if (S_ISFIFO(Mode))
return file_type::fifo_file;
else if (S_ISSOCK(Mode))
return file_type::socket_file;
else if (S_ISLNK(Mode))
return file_type::symlink_file;
return file_type::type_unknown;
}
static std::error_code fillStatus(int StatRet, const struct stat &Status,
file_status &Result) {
file_status &Result) {
if (StatRet != 0) {
std::error_code ec(errno, std::generic_category());
if (ec == std::errc::no_such_file_or_directory)
std::error_code EC(errno, std::generic_category());
if (EC == errc::no_such_file_or_directory)
Result = file_status(file_type::file_not_found);
else
Result = file_status(file_type::status_error);
return ec;
return EC;
}
file_type Type = file_type::type_unknown;
if (S_ISDIR(Status.st_mode))
Type = file_type::directory_file;
else if (S_ISREG(Status.st_mode))
Type = file_type::regular_file;
else if (S_ISBLK(Status.st_mode))
Type = file_type::block_file;
else if (S_ISCHR(Status.st_mode))
Type = file_type::character_file;
else if (S_ISFIFO(Status.st_mode))
Type = file_type::fifo_file;
else if (S_ISSOCK(Status.st_mode))
Type = file_type::socket_file;
else if (S_ISLNK(Status.st_mode))
Type = file_type::symlink_file;
uint32_t atime_nsec, mtime_nsec;
#if defined(__APPLE__)
atime_nsec = Status.st_atimespec.tv_nsec;
mtime_nsec = Status.st_mtimespec.tv_nsec;
#else
atime_nsec = Status.st_atim.tv_nsec;
mtime_nsec = Status.st_mtim.tv_nsec;
#endif
perms Perms = static_cast<perms>(Status.st_mode) & all_perms;
Result = file_status(Type, Perms, Status.st_dev, Status.st_nlink,
Status.st_ino, Status.st_atime, Status.st_mtime,
Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev,
Status.st_nlink, Status.st_ino,
Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec,
Status.st_uid, Status.st_gid, Status.st_size);
return std::error_code();
@@ -168,6 +199,71 @@ std::error_code status(int FD, file_status &Result) {
return fillStatus(StatRet, Status, Result);
}
std::error_code mapped_file_region::init(int FD, uint64_t Offset,
mapmode Mode) {
assert(Size != 0);
int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
#if defined(__APPLE__)
//----------------------------------------------------------------------
// Newer versions of MacOSX have a flag that will allow us to read from
// binaries whose code signature is invalid without crashing by using
// the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media
// is mapped we can avoid crashing and return zeroes to any pages we try
// to read if the media becomes unavailable by using the
// MAP_RESILIENT_MEDIA flag. These flags are only usable when mapping
// with PROT_READ, so take care not to specify them otherwise.
//----------------------------------------------------------------------
if (Mode == readonly) {
#if defined(MAP_RESILIENT_CODESIGN)
flags |= MAP_RESILIENT_CODESIGN;
#endif
#if defined(MAP_RESILIENT_MEDIA)
flags |= MAP_RESILIENT_MEDIA;
#endif
}
#endif // #if defined (__APPLE__)
Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset);
if (Mapping == MAP_FAILED)
return std::error_code(errno, std::generic_category());
return std::error_code();
}
mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
uint64_t offset, std::error_code &ec)
: Size(length), Mapping(), Mode(mode) {
(void)Mode;
ec = init(fd, offset, mode);
if (ec)
Mapping = nullptr;
}
mapped_file_region::~mapped_file_region() {
if (Mapping)
::munmap(Mapping, Size);
}
size_t mapped_file_region::size() const {
assert(Mapping && "Mapping failed but used anyway!");
return Size;
}
char *mapped_file_region::data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<char*>(Mapping);
}
const char *mapped_file_region::const_data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<const char*>(Mapping);
}
int mapped_file_region::alignment() {
return ::sysconf(_SC_PAGE_SIZE);
}
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path,
bool follow_symlinks) {
@@ -191,19 +287,25 @@ std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
return std::error_code();
}
std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
static file_type direntType(dirent* Entry) {
// Most platforms provide the file type in the dirent: Linux/BSD/Mac.
// The DTTOIF macro lets us reuse our status -> type conversion.
return typeForMode(DTTOIF(Entry->d_type));
}
std::error_code detail::directory_iterator_increment(detail::DirIterState &It) {
errno = 0;
dirent *cur_dir = ::readdir(reinterpret_cast<DIR *>(it.IterationHandle));
if (cur_dir == nullptr && errno != 0) {
dirent *CurDir = ::readdir(reinterpret_cast<DIR *>(It.IterationHandle));
if (CurDir == nullptr && errno != 0) {
return std::error_code(errno, std::generic_category());
} else if (cur_dir != nullptr) {
StringRef name(cur_dir->d_name, NAMLEN(cur_dir));
if ((name.size() == 1 && name[0] == '.') ||
(name.size() == 2 && name[0] == '.' && name[1] == '.'))
return directory_iterator_increment(it);
it.CurrentEntry.replace_filename(name);
} else if (CurDir != nullptr) {
StringRef Name(CurDir->d_name);
if ((Name.size() == 1 && Name[0] == '.') ||
(Name.size() == 2 && Name[0] == '.' && Name[1] == '.'))
return directory_iterator_increment(It);
It.CurrentEntry.replace_filename(Name, direntType(CurDir));
} else
return directory_iterator_destruct(it);
return directory_iterator_destruct(It);
return std::error_code();
}
@@ -224,14 +326,85 @@ static bool hasProcSelfFD() {
}
#endif
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
SmallVectorImpl<char> *RealPath) {
static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags,
FileAccess Access) {
int Result = 0;
if (Access == FA_Read)
Result |= O_RDONLY;
else if (Access == FA_Write)
Result |= O_WRONLY;
else if (Access == (FA_Read | FA_Write))
Result |= O_RDWR;
// This is for compatibility with old code that assumed F_Append implied
// would open an existing file. See Windows/Path.inc for a longer comment.
if (Flags & F_Append)
Disp = CD_OpenAlways;
if (Disp == CD_CreateNew) {
Result |= O_CREAT; // Create if it doesn't exist.
Result |= O_EXCL; // Fail if it does.
} else if (Disp == CD_CreateAlways) {
Result |= O_CREAT; // Create if it doesn't exist.
Result |= O_TRUNC; // Truncate if it does.
} else if (Disp == CD_OpenAlways) {
Result |= O_CREAT; // Create if it doesn't exist.
} else if (Disp == CD_OpenExisting) {
// Nothing special, just don't add O_CREAT and we get these semantics.
}
if (Flags & F_Append)
Result |= O_APPEND;
#ifdef O_CLOEXEC
if (!(Flags & OF_ChildInherit))
Result |= O_CLOEXEC;
#endif
return Result;
}
std::error_code openFile(const Twine &Name, int &ResultFD,
CreationDisposition Disp, FileAccess Access,
OpenFlags Flags, unsigned Mode) {
int OpenFlags = nativeOpenFlags(Disp, Flags, Access);
SmallString<128> Storage;
StringRef P = Name.toNullTerminatedStringRef(Storage);
while ((ResultFD = open(P.begin(), O_RDONLY)) < 0) {
if (errno != EINTR)
return std::error_code(errno, std::generic_category());
// Call ::open in a lambda to avoid overload resolution in RetryAfterSignal
// when open is overloaded, such as in Bionic.
auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); };
if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0)
return std::error_code(errno, std::generic_category());
#ifndef O_CLOEXEC
if (!(Flags & OF_ChildInherit)) {
int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC);
(void)r;
assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed");
}
#endif
return std::error_code();
}
Expected<int> openNativeFile(const Twine &Name, CreationDisposition Disp,
FileAccess Access, OpenFlags Flags,
unsigned Mode) {
int FD;
std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode);
if (EC)
return errorCodeToError(EC);
return FD;
}
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
OpenFlags Flags,
SmallVectorImpl<char> *RealPath) {
std::error_code EC =
openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666);
if (EC)
return EC;
// Attempt to get the real name of the file, if the user asked
if(!RealPath)
return std::error_code();
@@ -251,6 +424,9 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD,
if (CharCount > 0)
RealPath->append(Buffer, Buffer + CharCount);
} else {
SmallString<128> Storage;
StringRef P = Name.toNullTerminatedStringRef(Storage);
// Use ::realpath to get the real path name
if (::realpath(P.begin(), Buffer) != nullptr)
RealPath->append(Buffer, Buffer + strlen(Buffer));
@@ -259,38 +435,18 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD,
return std::error_code();
}
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
OpenFlags Flags, unsigned Mode) {
// Verify that we don't have both "append" and "excl".
assert((!(Flags & F_Excl) || !(Flags & F_Append)) &&
"Cannot specify both 'excl' and 'append' file creation flags!");
Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
SmallVectorImpl<char> *RealPath) {
file_t ResultFD;
std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath);
if (EC)
return errorCodeToError(EC);
return ResultFD;
}
int OpenFlags = O_CREAT;
#ifdef O_CLOEXEC
OpenFlags |= O_CLOEXEC;
#endif
if (Flags & F_RW)
OpenFlags |= O_RDWR;
else
OpenFlags |= O_WRONLY;
if (Flags & F_Append)
OpenFlags |= O_APPEND;
else if (!(Flags & F_NoTrunc))
OpenFlags |= O_TRUNC;
if (Flags & F_Excl)
OpenFlags |= O_EXCL;
SmallString<128> Storage;
StringRef P = Name.toNullTerminatedStringRef(Storage);
while ((ResultFD = open(P.begin(), OpenFlags, Mode)) < 0) {
if (errno != EINTR)
return std::error_code(errno, std::generic_category());
}
return std::error_code();
void closeFile(file_t &F) {
::close(F);
F = kInvalidFile;
}
} // end namespace fs
@@ -298,13 +454,18 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
namespace path {
bool home_directory(SmallVectorImpl<char> &result) {
if (char *RequestedDir = std::getenv("HOME")) {
result.clear();
result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
return true;
char *RequestedDir = getenv("HOME");
if (!RequestedDir) {
struct passwd *pw = getpwuid(getuid());
if (pw && pw->pw_dir)
RequestedDir = pw->pw_dir;
}
if (!RequestedDir)
return false;
return false;
result.clear();
result.append(RequestedDir, RequestedDir + strlen(RequestedDir));
return true;
}
static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) {
@@ -332,29 +493,6 @@ static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) {
return false;
}
static bool getUserCacheDir(SmallVectorImpl<char> &Result) {
// First try using XDG_CACHE_HOME env variable,
// as specified in XDG Base Directory Specification at
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
if (const char *XdgCacheDir = std::getenv("XDG_CACHE_HOME")) {
Result.clear();
Result.append(XdgCacheDir, XdgCacheDir + strlen(XdgCacheDir));
return true;
}
// Try Darwin configuration query
if (getDarwinConfDir(false, Result))
return true;
// Use "$HOME/.cache" if $HOME is available
if (home_directory(Result)) {
append(Result, ".cache");
return true;
}
return false;
}
static const char *getEnvTempDir() {
// Check whether the temporary directory is specified by an environment
// variable.

View File

@@ -17,6 +17,7 @@
//===----------------------------------------------------------------------===//
#include "wpi/STLExtras.h"
#include "wpi/ConvertUTF.h"
#include "wpi/WindowsError.h"
#include <fcntl.h>
#include <io.h>
@@ -31,6 +32,12 @@
#undef max
// MinGW doesn't define this.
#ifndef _ERRNO_T_DEFINED
#define _ERRNO_T_DEFINED
typedef int errno_t;
#endif
#ifdef _MSC_VER
# pragma comment(lib, "shell32.lib")
# pragma comment(lib, "ole32.lib")
@@ -116,6 +123,8 @@ std::error_code widenPath(const Twine &Path8,
namespace fs {
const file_t kInvalidFile = INVALID_HANDLE_VALUE;
UniqueID file_status::getUniqueID() const {
// The file is uniquely identified by the volume serial number along
// with the 64-bit file identifier.
@@ -125,6 +134,24 @@ UniqueID file_status::getUniqueID() const {
return UniqueID(VolumeSerialNumber, FileID);
}
TimePoint<> basic_file_status::getLastAccessedTime() const {
FILETIME Time;
Time.dwLowDateTime = LastAccessedTimeLow;
Time.dwHighDateTime = LastAccessedTimeHigh;
return toTimePoint(Time);
}
TimePoint<> basic_file_status::getLastModificationTime() const {
FILETIME Time;
Time.dwLowDateTime = LastWriteTimeLow;
Time.dwHighDateTime = LastWriteTimeHigh;
return toTimePoint(Time);
}
uint32_t file_status::getLinkCount() const {
return NumLinks;
}
std::error_code current_path(SmallVectorImpl<char> &result) {
SmallVector<wchar_t, MAX_PATH> cur_path;
DWORD len = MAX_PATH;
@@ -147,6 +174,51 @@ std::error_code current_path(SmallVectorImpl<char> &result) {
return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result);
}
static std::error_code realPathFromHandle(HANDLE H,
SmallVectorImpl<wchar_t> &Buffer) {
DWORD CountChars = ::GetFinalPathNameByHandleW(
H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
if (CountChars > Buffer.capacity()) {
// The buffer wasn't big enough, try again. In this case the return value
// *does* indicate the size of the null terminator.
Buffer.reserve(CountChars);
CountChars = ::GetFinalPathNameByHandleW(
H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED);
}
if (CountChars == 0)
return mapWindowsError(GetLastError());
Buffer.set_size(CountChars);
return std::error_code();
}
static std::error_code realPathFromHandle(HANDLE H,
SmallVectorImpl<char> &RealPath) {
RealPath.clear();
SmallVector<wchar_t, MAX_PATH> Buffer;
if (std::error_code EC = realPathFromHandle(H, Buffer))
return EC;
const wchar_t *Data = Buffer.data();
DWORD CountChars = Buffer.size();
if (CountChars >= 4) {
if (0 == ::memcmp(Data, L"\\\\?\\", 8)) {
CountChars -= 4;
Data += 4;
}
}
// Convert the result from UTF-16 to UTF-8.
return UTF16ToUTF8(Data, CountChars, RealPath);
}
static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) {
FILE_DISPOSITION_INFO Disposition;
Disposition.DeleteFile = Delete;
if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition,
sizeof(Disposition)))
return mapWindowsError(::GetLastError());
return std::error_code();
}
std::error_code access(const Twine &Path, AccessMode Mode) {
SmallVector<wchar_t, 128> PathUtf16;
@@ -162,11 +234,11 @@ std::error_code access(const Twine &Path, AccessMode Mode) {
if (LastError != ERROR_FILE_NOT_FOUND &&
LastError != ERROR_PATH_NOT_FOUND)
return mapWindowsError(LastError);
return std::make_error_code(std::errc::no_such_file_or_directory);
return errc::no_such_file_or_directory;
}
if (Mode == AccessMode::Write && (Attributes & FILE_ATTRIBUTE_READONLY))
return std::make_error_code(std::errc::permission_denied);
return errc::permission_denied;
return std::error_code();
}
@@ -315,6 +387,143 @@ std::error_code status(int FD, file_status &Result) {
return getStatus(FileHandle, Result);
}
std::error_code mapped_file_region::init(int FD, uint64_t Offset,
mapmode Mode) {
this->Mode = Mode;
HANDLE OrigFileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD));
if (OrigFileHandle == INVALID_HANDLE_VALUE)
return make_error_code(errc::bad_file_descriptor);
DWORD flprotect;
switch (Mode) {
case readonly: flprotect = PAGE_READONLY; break;
case readwrite: flprotect = PAGE_READWRITE; break;
case priv: flprotect = PAGE_WRITECOPY; break;
}
HANDLE FileMappingHandle =
::CreateFileMappingW(OrigFileHandle, 0, flprotect,
Hi_32(Size),
Lo_32(Size),
0);
if (FileMappingHandle == NULL) {
std::error_code ec = mapWindowsError(GetLastError());
return ec;
}
DWORD dwDesiredAccess;
switch (Mode) {
case readonly: dwDesiredAccess = FILE_MAP_READ; break;
case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break;
case priv: dwDesiredAccess = FILE_MAP_COPY; break;
}
Mapping = ::MapViewOfFile(FileMappingHandle,
dwDesiredAccess,
Offset >> 32,
Offset & 0xffffffff,
Size);
if (Mapping == NULL) {
std::error_code ec = mapWindowsError(GetLastError());
::CloseHandle(FileMappingHandle);
return ec;
}
if (Size == 0) {
MEMORY_BASIC_INFORMATION mbi;
SIZE_T Result = VirtualQuery(Mapping, &mbi, sizeof(mbi));
if (Result == 0) {
std::error_code ec = mapWindowsError(GetLastError());
::UnmapViewOfFile(Mapping);
::CloseHandle(FileMappingHandle);
return ec;
}
Size = mbi.RegionSize;
}
// Close the file mapping handle, as it's kept alive by the file mapping. But
// neither the file mapping nor the file mapping handle keep the file handle
// alive, so we need to keep a reference to the file in case all other handles
// are closed and the file is deleted, which may cause invalid data to be read
// from the file.
::CloseHandle(FileMappingHandle);
if (!::DuplicateHandle(::GetCurrentProcess(), OrigFileHandle,
::GetCurrentProcess(), &FileHandle, 0, 0,
DUPLICATE_SAME_ACCESS)) {
std::error_code ec = mapWindowsError(GetLastError());
::UnmapViewOfFile(Mapping);
return ec;
}
return std::error_code();
}
mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length,
uint64_t offset, std::error_code &ec)
: Size(length), Mapping() {
ec = init(fd, offset, mode);
if (ec)
Mapping = 0;
}
static bool hasFlushBufferKernelBug() {
static bool Ret{GetWindowsOSVersion() < wpi::VersionTuple(10, 0, 0, 17763)};
return Ret;
}
static bool isEXE(StringRef Magic) {
static const char PEMagic[] = {'P', 'E', '\0', '\0'};
if (Magic.startswith(StringRef("MZ")) && Magic.size() >= 0x3c + 4) {
uint32_t off = read32le(Magic.data() + 0x3c);
// PE/COFF file, either EXE or DLL.
if (Magic.substr(off).startswith(StringRef(PEMagic, sizeof(PEMagic))))
return true;
}
return false;
}
mapped_file_region::~mapped_file_region() {
if (Mapping) {
bool Exe = isEXE(StringRef((char *)Mapping, Size));
::UnmapViewOfFile(Mapping);
if (Mode == mapmode::readwrite && Exe && hasFlushBufferKernelBug()) {
// There is a Windows kernel bug, the exact trigger conditions of which
// are not well understood. When triggered, dirty pages are not properly
// flushed and subsequent process's attempts to read a file can return
// invalid data. Calling FlushFileBuffers on the write handle is
// sufficient to ensure that this bug is not triggered.
// The bug only occurs when writing an executable and executing it right
// after, under high I/O pressure.
::FlushFileBuffers(FileHandle);
}
::CloseHandle(FileHandle);
}
}
size_t mapped_file_region::size() const {
assert(Mapping && "Mapping failed but used anyway!");
return Size;
}
char *mapped_file_region::data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<char*>(Mapping);
}
const char *mapped_file_region::const_data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<const char*>(Mapping);
}
int mapped_file_region::alignment() {
SYSTEM_INFO SysInfo;
::GetSystemInfo(&SysInfo);
return SysInfo.dwAllocationGranularity;
}
static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) {
return basic_file_status(file_type_from_attrs(FindData->dwFileAttributes),
perms_from_attrs(FindData->dwFileAttributes),
@@ -325,28 +534,28 @@ static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) {
FindData->nFileSizeHigh, FindData->nFileSizeLow);
}
std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path,
bool follow_symlinks) {
SmallVector<wchar_t, 128> path_utf16;
std::error_code detail::directory_iterator_construct(detail::DirIterState &IT,
StringRef Path,
bool FollowSymlinks) {
SmallVector<wchar_t, 128> PathUTF16;
if (std::error_code ec = widenPath(path, path_utf16))
return ec;
if (std::error_code EC = widenPath(Path, PathUTF16))
return EC;
// Convert path to the format that Windows is happy with.
if (path_utf16.size() > 0 &&
!is_separator(path_utf16[path.size() - 1]) &&
path_utf16[path.size() - 1] != L':') {
path_utf16.push_back(L'\\');
path_utf16.push_back(L'*');
if (PathUTF16.size() > 0 &&
!is_separator(PathUTF16[Path.size() - 1]) &&
PathUTF16[Path.size() - 1] != L':') {
PathUTF16.push_back(L'\\');
PathUTF16.push_back(L'*');
} else {
path_utf16.push_back(L'*');
PathUTF16.push_back(L'*');
}
// Get the first directory entry.
WIN32_FIND_DATAW FirstFind;
ScopedFindHandle FindHandle(::FindFirstFileExW(
c_str(path_utf16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch,
c_str(PathUTF16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch,
NULL, FIND_FIRST_EX_LARGE_FETCH));
if (!FindHandle)
return mapWindowsError(::GetLastError());
@@ -359,43 +568,45 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
DWORD LastError = ::GetLastError();
// Check for end.
if (LastError == ERROR_NO_MORE_FILES)
return detail::directory_iterator_destruct(it);
return detail::directory_iterator_destruct(IT);
return mapWindowsError(LastError);
} else
FilenameLen = ::wcslen(FirstFind.cFileName);
// Construct the current directory entry.
SmallString<128> directory_entry_name_utf8;
if (std::error_code ec =
SmallString<128> DirectoryEntryNameUTF8;
if (std::error_code EC =
UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName),
directory_entry_name_utf8))
return ec;
DirectoryEntryNameUTF8))
return EC;
it.IterationHandle = intptr_t(FindHandle.take());
SmallString<128> directory_entry_path(path);
path::append(directory_entry_path, directory_entry_name_utf8);
it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks,
status_from_find_data(&FirstFind));
IT.IterationHandle = intptr_t(FindHandle.take());
SmallString<128> DirectoryEntryPath(Path);
path::append(DirectoryEntryPath, DirectoryEntryNameUTF8);
IT.CurrentEntry =
directory_entry(DirectoryEntryPath, FollowSymlinks,
file_type_from_attrs(FirstFind.dwFileAttributes),
status_from_find_data(&FirstFind));
return std::error_code();
}
std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) {
if (it.IterationHandle != 0)
std::error_code detail::directory_iterator_destruct(detail::DirIterState &IT) {
if (IT.IterationHandle != 0)
// Closes the handle if it's valid.
ScopedFindHandle close(HANDLE(it.IterationHandle));
it.IterationHandle = 0;
it.CurrentEntry = directory_entry();
ScopedFindHandle close(HANDLE(IT.IterationHandle));
IT.IterationHandle = 0;
IT.CurrentEntry = directory_entry();
return std::error_code();
}
std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
std::error_code detail::directory_iterator_increment(detail::DirIterState &IT) {
WIN32_FIND_DATAW FindData;
if (!::FindNextFileW(HANDLE(it.IterationHandle), &FindData)) {
if (!::FindNextFileW(HANDLE(IT.IterationHandle), &FindData)) {
DWORD LastError = ::GetLastError();
// Check for end.
if (LastError == ERROR_NO_MORE_FILES)
return detail::directory_iterator_destruct(it);
return detail::directory_iterator_destruct(IT);
return mapWindowsError(LastError);
}
@@ -403,16 +614,18 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) {
if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') ||
(FilenameLen == 2 && FindData.cFileName[0] == L'.' &&
FindData.cFileName[1] == L'.'))
return directory_iterator_increment(it);
return directory_iterator_increment(IT);
SmallString<128> directory_entry_path_utf8;
if (std::error_code ec =
SmallString<128> DirectoryEntryPathUTF8;
if (std::error_code EC =
UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName),
directory_entry_path_utf8))
return ec;
DirectoryEntryPathUTF8))
return EC;
it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8),
status_from_find_data(&FindData));
IT.CurrentEntry.replace_filename(
Twine(DirectoryEntryPathUTF8),
file_type_from_attrs(FindData.dwFileAttributes),
status_from_find_data(&FindData));
return std::error_code();
}
@@ -420,18 +633,82 @@ ErrorOr<basic_file_status> directory_entry::status() const {
return Status;
}
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
SmallVectorImpl<char> *RealPath) {
ResultFD = -1;
SmallVector<wchar_t, 128> PathUTF16;
static std::error_code nativeFileToFd(Expected<HANDLE> H, int &ResultFD,
OpenFlags Flags) {
int CrtOpenFlags = 0;
if (Flags & OF_Append)
CrtOpenFlags |= _O_APPEND;
if (Flags & OF_Text)
CrtOpenFlags |= _O_TEXT;
ResultFD = -1;
if (!H)
return errorToErrorCode(H.takeError());
ResultFD = ::_open_osfhandle(intptr_t(*H), CrtOpenFlags);
if (ResultFD == -1) {
::CloseHandle(*H);
return mapWindowsError(ERROR_INVALID_HANDLE);
}
return std::error_code();
}
static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) {
// This is a compatibility hack. Really we should respect the creation
// disposition, but a lot of old code relied on the implicit assumption that
// OF_Append implied it would open an existing file. Since the disposition is
// now explicit and defaults to CD_CreateAlways, this assumption would cause
// any usage of OF_Append to append to a new file, even if the file already
// existed. A better solution might have two new creation dispositions:
// CD_AppendAlways and CD_AppendNew. This would also address the problem of
// OF_Append being used on a read-only descriptor, which doesn't make sense.
if (Flags & OF_Append)
return OPEN_ALWAYS;
switch (Disp) {
case CD_CreateAlways:
return CREATE_ALWAYS;
case CD_CreateNew:
return CREATE_NEW;
case CD_OpenAlways:
return OPEN_ALWAYS;
case CD_OpenExisting:
return OPEN_EXISTING;
}
wpi_unreachable("unreachable!");
}
static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) {
DWORD Result = 0;
if (Access & FA_Read)
Result |= GENERIC_READ;
if (Access & FA_Write)
Result |= GENERIC_WRITE;
if (Flags & OF_Delete)
Result |= DELETE;
if (Flags & OF_UpdateAtime)
Result |= FILE_WRITE_ATTRIBUTES;
return Result;
}
static std::error_code openNativeFileInternal(const Twine &Name,
file_t &ResultFile, DWORD Disp,
DWORD Access, DWORD Flags,
bool Inherit = false) {
SmallVector<wchar_t, 128> PathUTF16;
if (std::error_code EC = widenPath(Name, PathUTF16))
return EC;
SECURITY_ATTRIBUTES SA;
SA.nLength = sizeof(SA);
SA.lpSecurityDescriptor = nullptr;
SA.bInheritHandle = Inherit;
HANDLE H =
::CreateFileW(PathUTF16.begin(), GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
::CreateFileW(PathUTF16.begin(), Access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA,
Disp, Flags, NULL);
if (H == INVALID_HANDLE_VALUE) {
DWORD LastError = ::GetLastError();
std::error_code EC = mapWindowsError(LastError);
@@ -441,98 +718,102 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD,
if (LastError != ERROR_ACCESS_DENIED)
return EC;
if (is_directory(Name))
return std::make_error_code(std::errc::is_a_directory);
return make_error_code(errc::is_a_directory);
return EC;
}
ResultFile = H;
return std::error_code();
}
ResultFD = ::_open_osfhandle(intptr_t(H), 0);
if (ResultFD == -1) {
::CloseHandle(H);
return mapWindowsError(ERROR_INVALID_HANDLE);
}
Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
FileAccess Access, OpenFlags Flags,
unsigned Mode) {
// Verify that we don't have both "append" and "excl".
assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) &&
"Cannot specify both 'CreateNew' and 'Append' file creation flags!");
// Fetch the real name of the file, if the user asked
if (RealPath) {
RealPath->clear();
wchar_t RealPathUTF16[MAX_PATH];
DWORD CountChars =
::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH,
FILE_NAME_NORMALIZED);
if (CountChars > 0 && CountChars < MAX_PATH) {
// Convert the result from UTF-16 to UTF-8.
SmallString<MAX_PATH> RealPathUTF8;
if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8))
RealPath->append(RealPathUTF8.data(),
RealPathUTF8.data() + strlen(RealPathUTF8.data()));
DWORD NativeDisp = nativeDisposition(Disp, Flags);
DWORD NativeAccess = nativeAccess(Access, Flags);
bool Inherit = false;
if (Flags & OF_ChildInherit)
Inherit = true;
file_t Result;
std::error_code EC = openNativeFileInternal(
Name, Result, NativeDisp, NativeAccess, FILE_ATTRIBUTE_NORMAL, Inherit);
if (EC)
return errorCodeToError(EC);
if (Flags & OF_UpdateAtime) {
FILETIME FileTime;
SYSTEMTIME SystemTime;
GetSystemTime(&SystemTime);
if (SystemTimeToFileTime(&SystemTime, &FileTime) == 0 ||
SetFileTime(Result, NULL, &FileTime, NULL) == 0) {
DWORD LastError = ::GetLastError();
::CloseHandle(Result);
return errorCodeToError(mapWindowsError(LastError));
}
}
return std::error_code();
if (Flags & OF_Delete) {
if ((EC = setDeleteDisposition(Result, true))) {
::CloseHandle(Result);
return errorCodeToError(EC);
}
}
return Result;
}
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
OpenFlags Flags, unsigned Mode) {
// Verify that we don't have both "append" and "excl".
assert((!(Flags & F_Excl) || !(Flags & F_Append)) &&
"Cannot specify both 'excl' and 'append' file creation flags!");
std::error_code openFile(const Twine &Name, int &ResultFD,
CreationDisposition Disp, FileAccess Access,
OpenFlags Flags, unsigned int Mode) {
Expected<file_t> Result = openNativeFile(Name, Disp, Access, Flags);
if (!Result)
return errorToErrorCode(Result.takeError());
ResultFD = -1;
SmallVector<wchar_t, 128> PathUTF16;
if (std::error_code EC = widenPath(Name, PathUTF16))
return EC;
DWORD CreationDisposition;
if (Flags & F_Excl)
CreationDisposition = CREATE_NEW;
else if ((Flags & F_Append) || (Flags & F_NoTrunc))
CreationDisposition = OPEN_ALWAYS;
else
CreationDisposition = CREATE_ALWAYS;
DWORD Access = GENERIC_WRITE;
DWORD Attributes = FILE_ATTRIBUTE_NORMAL;
if (Flags & F_RW)
Access |= GENERIC_READ;
if (Flags & F_Delete) {
Access |= DELETE;
Attributes |= FILE_FLAG_DELETE_ON_CLOSE;
}
HANDLE H =
::CreateFileW(PathUTF16.data(), Access,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, CreationDisposition, Attributes, NULL);
if (H == INVALID_HANDLE_VALUE) {
DWORD LastError = ::GetLastError();
std::error_code EC = mapWindowsError(LastError);
// Provide a better error message when trying to open directories.
// This only runs if we failed to open the file, so there is probably
// no performances issues.
if (LastError != ERROR_ACCESS_DENIED)
return EC;
if (is_directory(Name))
return std::make_error_code(std::errc::is_a_directory);
return EC;
}
int OpenFlags = 0;
if (Flags & F_Append)
OpenFlags |= _O_APPEND;
if (Flags & F_Text)
OpenFlags |= _O_TEXT;
ResultFD = ::_open_osfhandle(intptr_t(H), OpenFlags);
if (ResultFD == -1) {
::CloseHandle(H);
return mapWindowsError(ERROR_INVALID_HANDLE);
}
return std::error_code();
return nativeFileToFd(*Result, ResultFD, Flags);
}
static std::error_code directoryRealPath(const Twine &Name,
SmallVectorImpl<char> &RealPath) {
file_t File;
std::error_code EC = openNativeFileInternal(
Name, File, OPEN_EXISTING, GENERIC_READ, FILE_FLAG_BACKUP_SEMANTICS);
if (EC)
return EC;
EC = realPathFromHandle(File, RealPath);
::CloseHandle(File);
return EC;
}
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
OpenFlags Flags,
SmallVectorImpl<char> *RealPath) {
Expected<HANDLE> NativeFile = openNativeFileForRead(Name, Flags, RealPath);
return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None);
}
Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
SmallVectorImpl<char> *RealPath) {
Expected<file_t> Result =
openNativeFile(Name, CD_OpenExisting, FA_Read, Flags);
// Fetch the real name of the file, if the user asked
if (Result && RealPath)
realPathFromHandle(*Result, *RealPath);
return Result;
}
void closeFile(file_t &F) {
::CloseHandle(F);
F = kInvalidFile;
}
} // end namespace fs
namespace path {
@@ -547,10 +828,6 @@ static bool getKnownFolderPath(KNOWNFOLDERID folderId,
return ok;
}
bool getUserCacheDir(SmallVectorImpl<char> &Result) {
return getKnownFolderPath(FOLDERID_LocalAppData, Result);
}
bool home_directory(SmallVectorImpl<char> &result) {
return getKnownFolderPath(FOLDERID_Profile, result);
}

View File

@@ -38,11 +38,40 @@
#include "wpi/StringExtras.h"
#include "wpi/StringRef.h"
#include "wpi/Twine.h"
#include "wpi/Chrono.h"
#include "wpi/Compiler.h"
#include "wpi/VersionTuple.h"
#include <cassert>
#include <string>
#include <system_error>
#define WIN32_NO_STATUS
#include <windows.h>
#undef WIN32_NO_STATUS
#include <winternl.h>
#include <ntstatus.h>
namespace wpi {
/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses
/// RtlGetVersion or GetVersionEx under the hood depending on what is available.
/// GetVersionEx is deprecated, but this API exposes the build number which can
/// be useful for working around certain kernel bugs.
inline wpi::VersionTuple GetWindowsOSVersion() {
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
if (hMod) {
auto getVer = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (getVer) {
RTL_OSVERSIONINFOEXW info{};
info.dwOSVersionInfoSize = sizeof(info);
if (getVer((PRTL_OSVERSIONINFOW)&info) == ((NTSTATUS)0x00000000L)) {
return wpi::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0,
info.dwBuildNumber);
}
}
}
return wpi::VersionTuple(0, 0, 0, 0);
}
/// Determines if the program is running on Windows 8 or newer. This
/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended
@@ -50,20 +79,7 @@
/// yet have VersionHelpers.h, so we have our own helper.
inline bool RunningWindows8OrGreater() {
// Windows 8 is version 6.2, service pack 0.
OSVERSIONINFOEXW osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
osvi.dwMajorVersion = 6;
osvi.dwMinorVersion = 2;
osvi.wServicePackMajor = 0;
DWORDLONG Mask = 0;
Mask = VerSetConditionMask(Mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
Mask = VerSetConditionMask(Mask, VER_MINORVERSION, VER_GREATER_EQUAL);
Mask = VerSetConditionMask(Mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION |
VER_SERVICEPACKMAJOR,
Mask) != FALSE;
return GetWindowsOSVersion() >= wpi::VersionTuple(6, 2, 0, 0);
}
inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) {
@@ -90,8 +106,8 @@ class ScopedHandle {
typedef typename HandleTraits::handle_type handle_type;
handle_type Handle;
ScopedHandle(const ScopedHandle &other); // = delete;
void operator=(const ScopedHandle &other); // = delete;
ScopedHandle(const ScopedHandle &other) = delete;
void operator=(const ScopedHandle &other) = delete;
public:
ScopedHandle()
: Handle(HandleTraits::GetInvalid()) {}
@@ -179,7 +195,6 @@ typedef ScopedHandle<RegTraits> ScopedRegHandle;
typedef ScopedHandle<FindHandleTraits> ScopedFindHandle;
typedef ScopedHandle<JobHandleTraits> ScopedJobHandle;
namespace wpi {
template <class T>
class SmallVectorImpl;
@@ -192,21 +207,39 @@ c_str(SmallVectorImpl<T> &str) {
}
namespace sys {
namespace path {
std::error_code widenPath(const Twine &Path8,
SmallVectorImpl<wchar_t> &Path16);
} // end namespace path
namespace windows {
std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
/// Convert to UTF16 from the current code page used in the system
std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
/// Convert from UTF16 to the current code page used in the system
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
} // end namespace windows
inline std::chrono::nanoseconds toDuration(FILETIME Time) {
ULARGE_INTEGER TimeInteger;
TimeInteger.LowPart = Time.dwLowDateTime;
TimeInteger.HighPart = Time.dwHighDateTime;
// FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
return std::chrono::nanoseconds(100 * TimeInteger.QuadPart);
}
inline TimePoint<> toTimePoint(FILETIME Time) {
ULARGE_INTEGER TimeInteger;
TimeInteger.LowPart = Time.dwLowDateTime;
TimeInteger.HighPart = Time.dwHighDateTime;
// Adjust for different epoch
TimeInteger.QuadPart -= 11644473600ll * 10000000;
// FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond)
return TimePoint<>(std::chrono::nanoseconds(100 * TimeInteger.QuadPart));
}
inline FILETIME toFILETIME(TimePoint<> TP) {
ULARGE_INTEGER TimeInteger;
TimeInteger.QuadPart = TP.time_since_epoch().count() / 100;
TimeInteger.QuadPart += 11644473600ll * 10000000;
FILETIME Time;
Time.dwLowDateTime = TimeInteger.LowPart;
Time.dwHighDateTime = TimeInteger.HighPart;
return Time;
}
} // end namespace sys
} // end namespace wpi.

View File

@@ -17,6 +17,7 @@
#include "wpi/SmallVector.h"
#include "wpi/StringExtras.h"
#include "wpi/Compiler.h"
#include "wpi/ErrorHandling.h"
#include "wpi/FileSystem.h"
#include "wpi/Format.h"
#include "wpi/MathExtras.h"
@@ -56,6 +57,7 @@
#endif
#ifdef _WIN32
#include "wpi/ConvertUTF.h"
#include "Windows/WindowsSupport.h"
#endif
@@ -149,7 +151,7 @@ raw_ostream &raw_ostream::write_escaped(StringRef Str,
*this << '\\' << '"';
break;
default:
if (std::isprint(c)) {
if (isPrint(c)) {
*this << c;
break;
}
@@ -335,7 +337,7 @@ raw_ostream &raw_ostream::operator<<(const FormattedString &FS) {
break;
}
default:
assert(false && "Bad Justification");
wpi_unreachable("Bad Justification");
}
return *this;
}
@@ -419,7 +421,7 @@ raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) {
// Print the ASCII char values for each byte on this line
for (uint8_t Byte : Line) {
if (isprint(Byte))
if (isPrint(Byte))
*this << static_cast<char>(Byte);
else
*this << '.';
@@ -481,14 +483,18 @@ void format_object_base::home() {
//===----------------------------------------------------------------------===//
static int getFD(StringRef Filename, std::error_code &EC,
sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
sys::fs::OpenFlags Flags) {
assert((Access & sys::fs::FA_Write) &&
"Cannot make a raw_ostream from a read-only descriptor!");
// 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)) {
if (!(Flags & sys::fs::OF_Text)) {
#if defined(_WIN32)
_setmode(_fileno(stdout), _O_BINARY);
#endif
@@ -497,16 +503,39 @@ static int getFD(StringRef Filename, std::error_code &EC,
}
int FD;
EC = sys::fs::openFileForWrite(Filename, FD, Flags);
if (Access & sys::fs::FA_Read)
EC = sys::fs::openFileForReadWrite(Filename, FD, Disp, Flags);
else
EC = sys::fs::openFileForWrite(Filename, FD, Disp, Flags);
if (EC)
return -1;
return FD;
}
raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC)
: raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write,
sys::fs::OF_None) {}
raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::CreationDisposition Disp)
: raw_fd_ostream(Filename, EC, Disp, sys::fs::FA_Write, sys::fs::OF_None) {}
raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::FileAccess Access)
: raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, Access,
sys::fs::OF_None) {}
raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::OpenFlags Flags)
: raw_fd_ostream(getFD(Filename, EC, Flags), true) {}
: raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write,
Flags) {}
raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::CreationDisposition Disp,
sys::fs::FileAccess Access,
sys::fs::OpenFlags Flags)
: raw_fd_ostream(getFD(Filename, EC, Disp, Access, Flags), true) {}
/// FD is the file descriptor that this writes to. If ShouldClose is true, this
/// closes the file when the stream is destroyed.
@@ -526,6 +555,12 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered)
if (FD <= STDERR_FILENO)
ShouldClose = false;
#ifdef _WIN32
// Check if this is a console device. This is not equivalent to isatty.
IsWindowsConsole =
::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR;
#endif
// Get the starting position.
off_t loc = ::lseek(FD, 0, SEEK_CUR);
#ifdef _WIN32
@@ -554,27 +589,87 @@ raw_fd_ostream::~raw_fd_ostream() {
// 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);
}
#if defined(_WIN32)
// The most reliable way to print unicode in a Windows console is with
// WriteConsoleW. To use that, first transcode from UTF-8 to UTF-16. This
// assumes that LLVM programs always print valid UTF-8 to the console. The data
// might not be UTF-8 for two major reasons:
// 1. The program is printing binary (-filetype=obj -o -), in which case it
// would have been gibberish anyway.
// 2. The program is printing text in a semi-ascii compatible codepage like
// shift-jis or cp1252.
//
// Most LLVM programs don't produce non-ascii text unless they are quoting
// user source input. A well-behaved LLVM program should either validate that
// the input is UTF-8 or transcode from the local codepage to UTF-8 before
// quoting it. If they don't, this may mess up the encoding, but this is still
// probably the best compromise we can make.
static bool write_console_impl(int FD, StringRef Data) {
SmallVector<wchar_t, 256> WideText;
// Fall back to ::write if it wasn't valid UTF-8.
if (auto EC = sys::windows::UTF8ToUTF16(Data, WideText))
return false;
// On Windows 7 and earlier, WriteConsoleW has a low maximum amount of data
// that can be written to the console at a time.
size_t MaxWriteSize = WideText.size();
if (!RunningWindows8OrGreater())
MaxWriteSize = 32767;
size_t WCharsWritten = 0;
do {
size_t WCharsToWrite =
std::min(MaxWriteSize, WideText.size() - WCharsWritten);
DWORD ActuallyWritten;
bool Success =
::WriteConsoleW((HANDLE)::_get_osfhandle(FD), &WideText[WCharsWritten],
WCharsToWrite, &ActuallyWritten,
/*Reserved=*/nullptr);
// The most likely reason for WriteConsoleW to fail is that FD no longer
// points to a console. Fall back to ::write. If this isn't the first loop
// iteration, something is truly wrong.
if (!Success)
return false;
WCharsWritten += ActuallyWritten;
} while (WCharsWritten != WideText.size());
return true;
}
#endif
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(_WIN32)
// If this is a Windows console device, try re-encoding from UTF-8 to UTF-16
// and using WriteConsoleW. If that fails, fall back to plain write().
if (IsWindowsConsole)
if (write_console_impl(FD, StringRef(Ptr, Size)))
return;
#endif
// The maximum write size is limited to INT32_MAX. A write
// greater than SSIZE_MAX is implementation-defined in POSIX,
// and Windows _write requires 32 bit input.
size_t MaxWriteSize = INT32_MAX;
#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(_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 {
@@ -645,8 +740,17 @@ void raw_fd_ostream::pwrite_impl(const char *Ptr, size_t Size,
}
size_t raw_fd_ostream::preferred_buffer_size() const {
#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__minix)
// Windows and Minix have no st_blksize.
#if defined(_WIN32)
// Disable buffering for console devices. Console output is re-encoded from
// UTF-8 to UTF-16 on Windows, and buffering it would require us to split the
// buffer on a valid UTF-8 codepoint boundary. Terminal buffering is disabled
// below on most other OSs, so do the same thing on Windows and avoid that
// complexity.
if (IsWindowsConsole)
return 0;
return raw_ostream::preferred_buffer_size();
#elif !defined(__minix)
// Minix has no st_blksize.
assert(FD >= 0 && "File not yet open!");
struct stat statbuf;
if (fstat(FD, &statbuf) != 0)
@@ -791,3 +895,5 @@ void raw_null_ostream::pwrite_impl(const char * /*Ptr*/, size_t /*Size*/,
uint64_t /*Offset*/) {}
void raw_pwrite_stream::anchor() {}
void buffer_ostream::anchor() {}

View File

@@ -1,43 +0,0 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#include "wpi/memory.h"
#include <exception>
#include "wpi/raw_ostream.h"
namespace wpi {
void* CheckedCalloc(size_t num, size_t size) {
void* p = std::calloc(num, size);
if (!p) {
errs() << "FATAL: failed to allocate " << (num * size) << " bytes\n";
std::terminate();
}
return p;
}
void* CheckedMalloc(size_t size) {
void* p = std::malloc(size == 0 ? 1 : size);
if (!p) {
errs() << "FATAL: failed to allocate " << size << " bytes\n";
std::terminate();
}
return p;
}
void* CheckedRealloc(void* ptr, size_t size) {
void* p = std::realloc(ptr, size == 0 ? 1 : size);
if (!p) {
errs() << "FATAL: failed to allocate " << size << " bytes\n";
std::terminate();
}
return p;
}
} // namespace wpi

View File

@@ -34,7 +34,7 @@ namespace wpi {
template<std::size_t Alignment, std::size_t Size>
struct AlignedCharArray {
LLVM_ALIGNAS(Alignment) char buffer[Size];
alignas(Alignment) char buffer[Size];
};
#else // _MSC_VER

View File

@@ -26,6 +26,7 @@
#include <vector>
namespace wpi {
/// ArrayRef - Represent a constant reference to an array (0 or more elements
/// consecutively in memory), i.e. a start pointer and a length. It allows
/// various APIs to take consecutive elements easily and conveniently.

View File

@@ -0,0 +1,63 @@
//===- llvm/Support/Chrono.h - Utilities for Timing Manipulation-*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_CHRONO_H
#define WPIUTIL_WPI_CHRONO_H
#include "wpi/Compiler.h"
#include <chrono>
#include <ctime>
namespace wpi {
class raw_ostream;
namespace sys {
/// A time point on the system clock. This is provided for two reasons:
/// - to insulate us agains subtle differences in behavoir to differences in
/// system clock precision (which is implementation-defined and differs between
/// platforms).
/// - to shorten the type name
/// The default precision is nanoseconds. If need a specific precision specify
/// it explicitly. If unsure, use the default. If you need a time point on a
/// clock other than the system_clock, use std::chrono directly.
template <typename D = std::chrono::nanoseconds>
using TimePoint = std::chrono::time_point<std::chrono::system_clock, D>;
/// Convert a TimePoint to std::time_t
LLVM_ATTRIBUTE_ALWAYS_INLINE std::time_t toTimeT(TimePoint<> TP) {
using namespace std::chrono;
return system_clock::to_time_t(
time_point_cast<system_clock::time_point::duration>(TP));
}
/// Convert a std::time_t to a TimePoint
LLVM_ATTRIBUTE_ALWAYS_INLINE TimePoint<std::chrono::seconds>
toTimePoint(std::time_t T) {
using namespace std::chrono;
return time_point_cast<seconds>(system_clock::from_time_t(T));
}
/// Convert a std::time_t + nanoseconds to a TimePoint
LLVM_ATTRIBUTE_ALWAYS_INLINE TimePoint<>
toTimePoint(std::time_t T, uint32_t nsec) {
using namespace std::chrono;
return time_point_cast<nanoseconds>(system_clock::from_time_t(T))
+ nanoseconds(nsec);
}
} // namespace sys
raw_ostream &operator<<(raw_ostream &OS, sys::TimePoint<> TP);
} // namespace wpi
#endif // WPIUTIL_WPI_CHRONO_H

View File

@@ -287,6 +287,41 @@
#endif
#endif
/// LLVM_BUILTIN_TRAP - On compilers which support it, expands to an expression
/// which causes the program to exit abnormally.
#ifndef LLVM_BUILTIN_TRAP
#if __has_builtin(__builtin_trap) || LLVM_GNUC_PREREQ(4, 3, 0)
# define LLVM_BUILTIN_TRAP __builtin_trap()
#elif defined(_MSC_VER)
// The __debugbreak intrinsic is supported by MSVC, does not require forward
// declarations involving platform-specific typedefs (unlike RaiseException),
// results in a call to vectored exception handlers, and encodes to a short
// instruction that still causes the trapping behavior we want.
# define LLVM_BUILTIN_TRAP __debugbreak()
#else
# define LLVM_BUILTIN_TRAP *(volatile int*)0x11 = 0
#endif
#endif
/// LLVM_BUILTIN_DEBUGTRAP - On compilers which support it, expands to
/// an expression which causes the program to break while running
/// under a debugger.
#ifndef LLVM_BUILTIN_DEBUGTRAP
#if __has_builtin(__builtin_debugtrap)
# define LLVM_BUILTIN_DEBUGTRAP __builtin_debugtrap()
#elif defined(_MSC_VER)
// The __debugbreak intrinsic is supported by MSVC and breaks while
// running under the debugger, and also supports invoking a debugger
// when the OS is configured appropriately.
# define LLVM_BUILTIN_DEBUGTRAP __debugbreak()
#else
// Just continue execution when built with compilers that have no
// support. This is a debugging aid and not intended to force the
// program to abort if encountered.
# define LLVM_BUILTIN_DEBUGTRAP
#endif
#endif
/// \macro LLVM_ASSUME_ALIGNED
/// Returns a pointer with an assumed alignment.
#ifndef LLVM_ASSUME_ALIGNED

View File

@@ -95,6 +95,7 @@
#include <cstddef>
#include <string>
#include <system_error>
// Wrap everything in namespace wpi so that programs can link with wpiutil and
// their own version of the unicode libraries.
@@ -245,6 +246,21 @@ bool convertUTF16ToUTF8String(ArrayRef<UTF16> SrcUTF16,
bool convertUTF8ToUTF16String(StringRef SrcUTF8,
SmallVectorImpl<UTF16> &DstUTF16);
#if defined(_WIN32)
namespace sys {
namespace windows {
std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
/// Convert to UTF16 from the current code page used in the system
std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl<wchar_t> &utf16);
std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
/// Convert from UTF16 to the current code page used in the system
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
SmallVectorImpl<char> &utf8);
} // namespace windows
} // namespace sys
#endif
} /* end namespace wpi */
#endif

View File

@@ -46,9 +46,10 @@ struct DenseMapPair : public std::pair<KeyT, ValueT> {
} // end namespace detail
template <
typename KeyT, typename ValueT, typename KeyInfoT = DenseMapInfo<KeyT>,
typename Bucket = detail::DenseMapPair<KeyT, ValueT>, bool IsConst = false>
template <typename KeyT, typename ValueT,
typename KeyInfoT = DenseMapInfo<KeyT>,
typename Bucket = wpi::detail::DenseMapPair<KeyT, ValueT>,
bool IsConst = false>
class DenseMapIterator;
template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT,
@@ -389,7 +390,7 @@ protected:
setNumTombstones(other.getNumTombstones());
if (isPodLike<KeyT>::value && isPodLike<ValueT>::value)
memcpy(getBuckets(), other.getBuckets(),
memcpy(reinterpret_cast<void *>(getBuckets()), other.getBuckets(),
getNumBuckets() * sizeof(BucketT));
else
for (size_t i = 0; i < getNumBuckets(); ++i) {
@@ -627,9 +628,43 @@ public:
}
};
/// Equality comparison for DenseMap.
///
/// Iterates over elements of LHS confirming that each (key, value) pair in LHS
/// is also in RHS, and that no additional pairs are in RHS.
/// Equivalent to N calls to RHS.find and N value comparisons. Amortized
/// complexity is linear, worst case is O(N^2) (if every hash collides).
template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT,
typename BucketT>
bool operator==(
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &LHS,
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &RHS) {
if (LHS.size() != RHS.size())
return false;
for (auto &KV : LHS) {
auto I = RHS.find(KV.first);
if (I == RHS.end() || I->second != KV.second)
return false;
}
return true;
}
/// Inequality comparison for DenseMap.
///
/// Equivalent to !(LHS == RHS). See operator== for performance notes.
template <typename DerivedT, typename KeyT, typename ValueT, typename KeyInfoT,
typename BucketT>
bool operator!=(
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &LHS,
const DenseMapBase<DerivedT, KeyT, ValueT, KeyInfoT, BucketT> &RHS) {
return !(LHS == RHS);
}
template <typename KeyT, typename ValueT,
typename KeyInfoT = DenseMapInfo<KeyT>,
typename BucketT = detail::DenseMapPair<KeyT, ValueT>>
typename BucketT = wpi::detail::DenseMapPair<KeyT, ValueT>>
class DenseMap : public DenseMapBase<DenseMap<KeyT, ValueT, KeyInfoT, BucketT>,
KeyT, ValueT, KeyInfoT, BucketT> {
friend class DenseMapBase<DenseMap, KeyT, ValueT, KeyInfoT, BucketT>;
@@ -664,6 +699,11 @@ public:
this->insert(I, E);
}
DenseMap(std::initializer_list<typename BaseT::value_type> Vals) {
init(Vals.size());
this->insert(Vals.begin(), Vals.end());
}
~DenseMap() {
this->destroyAll();
operator delete(Buckets);
@@ -786,7 +826,7 @@ private:
template <typename KeyT, typename ValueT, unsigned InlineBuckets = 4,
typename KeyInfoT = DenseMapInfo<KeyT>,
typename BucketT = detail::DenseMapPair<KeyT, ValueT>>
typename BucketT = wpi::detail::DenseMapPair<KeyT, ValueT>>
class SmallDenseMap
: public DenseMapBase<
SmallDenseMap<KeyT, ValueT, InlineBuckets, KeyInfoT, BucketT>, KeyT,

View File

@@ -262,6 +262,13 @@ template <typename T> struct DenseMapInfo<ArrayRef<T>> {
}
};
template <> struct DenseMapInfo<hash_code> {
static inline hash_code getEmptyKey() { return hash_code(-1); }
static inline hash_code getTombstoneKey() { return hash_code(-2); }
static unsigned getHashValue(hash_code val) { return val; }
static bool isEqual(hash_code LHS, hash_code RHS) { return LHS == RHS; }
};
} // end namespace wpi
#endif // LLVM_ADT_DENSEMAPINFO_H

View File

@@ -0,0 +1,425 @@
//===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares generic functions to read and write endian specific data.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_ENDIAN_H
#define WPIUTIL_WPI_ENDIAN_H
#include "wpi/AlignOf.h"
#include "wpi/Compiler.h"
#include "wpi/SwapByteOrder.h"
#if defined(__linux__) || defined(__GNU__)
#include <endian.h>
#endif
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <type_traits>
namespace wpi {
namespace support {
enum endianness {big, little, native};
// These are named values for common alignments.
enum {aligned = 0, unaligned = 1};
namespace detail {
/// ::value is either alignment, or alignof(T) if alignment is 0.
template<class T, int alignment>
struct PickAlignment {
enum { value = alignment == 0 ? alignof(T) : alignment };
};
} // end namespace detail
namespace endian {
constexpr endianness system_endianness() {
#ifdef _WIN32
return little;
#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
return big;
#else
return little;
#endif
}
template <typename value_type>
inline value_type byte_swap(value_type value, endianness endian) {
if ((endian != native) && (endian != system_endianness()))
sys::swapByteOrder(value);
return value;
}
/// Swap the bytes of value to match the given endianness.
template<typename value_type, endianness endian>
inline value_type byte_swap(value_type value) {
return byte_swap(value, endian);
}
/// Read a value of a particular endianness from memory.
template <typename value_type, std::size_t alignment>
inline value_type read(const void *memory, endianness endian) {
value_type ret;
memcpy(&ret,
LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
sizeof(value_type));
return byte_swap<value_type>(ret, endian);
}
template<typename value_type,
endianness endian,
std::size_t alignment>
inline value_type read(const void *memory) {
return read<value_type, alignment>(memory, endian);
}
/// Read a value of a particular endianness from a buffer, and increment the
/// buffer past that value.
template <typename value_type, std::size_t alignment, typename CharT>
inline value_type readNext(const CharT *&memory, endianness endian) {
value_type ret = read<value_type, alignment>(memory, endian);
memory += sizeof(value_type);
return ret;
}
template<typename value_type, endianness endian, std::size_t alignment,
typename CharT>
inline value_type readNext(const CharT *&memory) {
return readNext<value_type, alignment, CharT>(memory, endian);
}
/// Write a value to memory with a particular endianness.
template <typename value_type, std::size_t alignment>
inline void write(void *memory, value_type value, endianness endian) {
value = byte_swap<value_type>(value, endian);
memcpy(LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
&value, sizeof(value_type));
}
template<typename value_type,
endianness endian,
std::size_t alignment>
inline void write(void *memory, value_type value) {
write<value_type, alignment>(memory, value, endian);
}
template <typename value_type>
using make_unsigned_t = typename std::make_unsigned<value_type>::type;
/// Read a value of a particular endianness from memory, for a location
/// that starts at the given bit offset within the first byte.
template <typename value_type, endianness endian, std::size_t alignment>
inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
assert(startBit < 8);
if (startBit == 0)
return read<value_type, endian, alignment>(memory);
else {
// Read two values and compose the result from them.
value_type val[2];
memcpy(&val[0],
LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
sizeof(value_type) * 2);
val[0] = byte_swap<value_type, endian>(val[0]);
val[1] = byte_swap<value_type, endian>(val[1]);
// Shift bits from the lower value into place.
make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
// Mask off upper bits after right shift in case of signed type.
make_unsigned_t<value_type> numBitsFirstVal =
(sizeof(value_type) * 8) - startBit;
lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1;
// Get the bits from the upper value.
make_unsigned_t<value_type> upperVal =
val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1);
// Shift them in to place.
upperVal <<= numBitsFirstVal;
return lowerVal | upperVal;
}
}
/// Write a value to memory with a particular endianness, for a location
/// that starts at the given bit offset within the first byte.
template <typename value_type, endianness endian, std::size_t alignment>
inline void writeAtBitAlignment(void *memory, value_type value,
uint64_t startBit) {
assert(startBit < 8);
if (startBit == 0)
write<value_type, endian, alignment>(memory, value);
else {
// Read two values and shift the result into them.
value_type val[2];
memcpy(&val[0],
LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
sizeof(value_type) * 2);
val[0] = byte_swap<value_type, endian>(val[0]);
val[1] = byte_swap<value_type, endian>(val[1]);
// Mask off any existing bits in the upper part of the lower value that
// we want to replace.
val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
make_unsigned_t<value_type> numBitsFirstVal =
(sizeof(value_type) * 8) - startBit;
make_unsigned_t<value_type> lowerVal = value;
if (startBit > 0) {
// Mask off the upper bits in the new value that are not going to go into
// the lower value. This avoids a left shift of a negative value, which
// is undefined behavior.
lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1);
// Now shift the new bits into place
lowerVal <<= startBit;
}
val[0] |= lowerVal;
// Mask off any existing bits in the lower part of the upper value that
// we want to replace.
val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1);
// Next shift the bits that go into the upper value into position.
make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal;
// Mask off upper bits after right shift in case of signed type.
upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
val[1] |= upperVal;
// Finally, rewrite values.
val[0] = byte_swap<value_type, endian>(val[0]);
val[1] = byte_swap<value_type, endian>(val[1]);
memcpy(LLVM_ASSUME_ALIGNED(
memory, (detail::PickAlignment<value_type, alignment>::value)),
&val[0], sizeof(value_type) * 2);
}
}
} // end namespace endian
namespace detail {
template<typename value_type,
endianness endian,
std::size_t alignment>
struct packed_endian_specific_integral {
packed_endian_specific_integral() = default;
explicit packed_endian_specific_integral(value_type val) { *this = val; }
operator value_type() const {
return endian::read<value_type, endian, alignment>(
(const void*)Value.buffer);
}
void operator=(value_type newValue) {
endian::write<value_type, endian, alignment>(
(void*)Value.buffer, newValue);
}
packed_endian_specific_integral &operator+=(value_type newValue) {
*this = *this + newValue;
return *this;
}
packed_endian_specific_integral &operator-=(value_type newValue) {
*this = *this - newValue;
return *this;
}
packed_endian_specific_integral &operator|=(value_type newValue) {
*this = *this | newValue;
return *this;
}
packed_endian_specific_integral &operator&=(value_type newValue) {
*this = *this & newValue;
return *this;
}
private:
AlignedCharArray<PickAlignment<value_type, alignment>::value,
sizeof(value_type)> Value;
public:
struct ref {
explicit ref(void *Ptr) : Ptr(Ptr) {}
operator value_type() const {
return endian::read<value_type, endian, alignment>(Ptr);
}
void operator=(value_type NewValue) {
endian::write<value_type, endian, alignment>(Ptr, NewValue);
}
private:
void *Ptr;
};
};
} // end namespace detail
using ulittle16_t =
detail::packed_endian_specific_integral<uint16_t, little, unaligned>;
using ulittle32_t =
detail::packed_endian_specific_integral<uint32_t, little, unaligned>;
using ulittle64_t =
detail::packed_endian_specific_integral<uint64_t, little, unaligned>;
using little16_t =
detail::packed_endian_specific_integral<int16_t, little, unaligned>;
using little32_t =
detail::packed_endian_specific_integral<int32_t, little, unaligned>;
using little64_t =
detail::packed_endian_specific_integral<int64_t, little, unaligned>;
using aligned_ulittle16_t =
detail::packed_endian_specific_integral<uint16_t, little, aligned>;
using aligned_ulittle32_t =
detail::packed_endian_specific_integral<uint32_t, little, aligned>;
using aligned_ulittle64_t =
detail::packed_endian_specific_integral<uint64_t, little, aligned>;
using aligned_little16_t =
detail::packed_endian_specific_integral<int16_t, little, aligned>;
using aligned_little32_t =
detail::packed_endian_specific_integral<int32_t, little, aligned>;
using aligned_little64_t =
detail::packed_endian_specific_integral<int64_t, little, aligned>;
using ubig16_t =
detail::packed_endian_specific_integral<uint16_t, big, unaligned>;
using ubig32_t =
detail::packed_endian_specific_integral<uint32_t, big, unaligned>;
using ubig64_t =
detail::packed_endian_specific_integral<uint64_t, big, unaligned>;
using big16_t =
detail::packed_endian_specific_integral<int16_t, big, unaligned>;
using big32_t =
detail::packed_endian_specific_integral<int32_t, big, unaligned>;
using big64_t =
detail::packed_endian_specific_integral<int64_t, big, unaligned>;
using aligned_ubig16_t =
detail::packed_endian_specific_integral<uint16_t, big, aligned>;
using aligned_ubig32_t =
detail::packed_endian_specific_integral<uint32_t, big, aligned>;
using aligned_ubig64_t =
detail::packed_endian_specific_integral<uint64_t, big, aligned>;
using aligned_big16_t =
detail::packed_endian_specific_integral<int16_t, big, aligned>;
using aligned_big32_t =
detail::packed_endian_specific_integral<int32_t, big, aligned>;
using aligned_big64_t =
detail::packed_endian_specific_integral<int64_t, big, aligned>;
using unaligned_uint16_t =
detail::packed_endian_specific_integral<uint16_t, native, unaligned>;
using unaligned_uint32_t =
detail::packed_endian_specific_integral<uint32_t, native, unaligned>;
using unaligned_uint64_t =
detail::packed_endian_specific_integral<uint64_t, native, unaligned>;
using unaligned_int16_t =
detail::packed_endian_specific_integral<int16_t, native, unaligned>;
using unaligned_int32_t =
detail::packed_endian_specific_integral<int32_t, native, unaligned>;
using unaligned_int64_t =
detail::packed_endian_specific_integral<int64_t, native, unaligned>;
namespace endian {
template <typename T> inline T read(const void *P, endianness E) {
return read<T, unaligned>(P, E);
}
template <typename T, endianness E> inline T read(const void *P) {
return *(const detail::packed_endian_specific_integral<T, E, unaligned> *)P;
}
inline uint16_t read16(const void *P, endianness E) {
return read<uint16_t>(P, E);
}
inline uint32_t read32(const void *P, endianness E) {
return read<uint32_t>(P, E);
}
inline uint64_t read64(const void *P, endianness E) {
return read<uint64_t>(P, E);
}
template <endianness E> inline uint16_t read16(const void *P) {
return read<uint16_t, E>(P);
}
template <endianness E> inline uint32_t read32(const void *P) {
return read<uint32_t, E>(P);
}
template <endianness E> inline uint64_t read64(const void *P) {
return read<uint64_t, E>(P);
}
inline uint16_t read16le(const void *P) { return read16<little>(P); }
inline uint32_t read32le(const void *P) { return read32<little>(P); }
inline uint64_t read64le(const void *P) { return read64<little>(P); }
inline uint16_t read16be(const void *P) { return read16<big>(P); }
inline uint32_t read32be(const void *P) { return read32<big>(P); }
inline uint64_t read64be(const void *P) { return read64<big>(P); }
template <typename T> inline void write(void *P, T V, endianness E) {
write<T, unaligned>(P, V, E);
}
template <typename T, endianness E> inline void write(void *P, T V) {
*(detail::packed_endian_specific_integral<T, E, unaligned> *)P = V;
}
inline void write16(void *P, uint16_t V, endianness E) {
write<uint16_t>(P, V, E);
}
inline void write32(void *P, uint32_t V, endianness E) {
write<uint32_t>(P, V, E);
}
inline void write64(void *P, uint64_t V, endianness E) {
write<uint64_t>(P, V, E);
}
template <endianness E> inline void write16(void *P, uint16_t V) {
write<uint16_t, E>(P, V);
}
template <endianness E> inline void write32(void *P, uint32_t V) {
write<uint32_t, E>(P, V);
}
template <endianness E> inline void write64(void *P, uint64_t V) {
write<uint64_t, E>(P, V);
}
inline void write16le(void *P, uint16_t V) { write16<little>(P, V); }
inline void write32le(void *P, uint32_t V) { write32<little>(P, V); }
inline void write64le(void *P, uint64_t V) { write64<little>(P, V); }
inline void write16be(void *P, uint16_t V) { write16<big>(P, V); }
inline void write32be(void *P, uint32_t V) { write32<big>(P, V); }
inline void write64be(void *P, uint64_t V) { write64<big>(P, V); }
} // end namespace endian
} // end namespace support
} // end namespace wpi
#endif // WPIUTIL_WPI_ENDIAN_H

View File

@@ -0,0 +1,87 @@
//===- llvm/Support/Errc.h - Defines the llvm::errc enum --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// While std::error_code works OK on all platforms we use, there are some
// some problems with std::errc that can be avoided by using our own
// enumeration:
//
// * std::errc is a namespace in some implementations. That meas that ADL
// doesn't work and it is sometimes necessary to write std::make_error_code
// or in templates:
// using std::make_error_code;
// make_error_code(...);
//
// with this enum it is safe to always just use make_error_code.
//
// * Some implementations define fewer names than others. This header has
// the intersection of all the ones we support.
//
// * std::errc is just marked with is_error_condition_enum. This means that
// common patters like AnErrorCode == errc::no_such_file_or_directory take
// 4 virtual calls instead of two comparisons.
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_ERRC_H
#define WPIUTIL_WPI_ERRC_H
#include <system_error>
namespace wpi {
enum class errc {
argument_list_too_long = int(std::errc::argument_list_too_long),
argument_out_of_domain = int(std::errc::argument_out_of_domain),
bad_address = int(std::errc::bad_address),
bad_file_descriptor = int(std::errc::bad_file_descriptor),
broken_pipe = int(std::errc::broken_pipe),
device_or_resource_busy = int(std::errc::device_or_resource_busy),
directory_not_empty = int(std::errc::directory_not_empty),
executable_format_error = int(std::errc::executable_format_error),
file_exists = int(std::errc::file_exists),
file_too_large = int(std::errc::file_too_large),
filename_too_long = int(std::errc::filename_too_long),
function_not_supported = int(std::errc::function_not_supported),
illegal_byte_sequence = int(std::errc::illegal_byte_sequence),
inappropriate_io_control_operation =
int(std::errc::inappropriate_io_control_operation),
interrupted = int(std::errc::interrupted),
invalid_argument = int(std::errc::invalid_argument),
invalid_seek = int(std::errc::invalid_seek),
io_error = int(std::errc::io_error),
is_a_directory = int(std::errc::is_a_directory),
no_child_process = int(std::errc::no_child_process),
no_lock_available = int(std::errc::no_lock_available),
no_space_on_device = int(std::errc::no_space_on_device),
no_such_device_or_address = int(std::errc::no_such_device_or_address),
no_such_device = int(std::errc::no_such_device),
no_such_file_or_directory = int(std::errc::no_such_file_or_directory),
no_such_process = int(std::errc::no_such_process),
not_a_directory = int(std::errc::not_a_directory),
not_enough_memory = int(std::errc::not_enough_memory),
not_supported = int(std::errc::not_supported),
operation_not_permitted = int(std::errc::operation_not_permitted),
permission_denied = int(std::errc::permission_denied),
read_only_file_system = int(std::errc::read_only_file_system),
resource_deadlock_would_occur = int(std::errc::resource_deadlock_would_occur),
resource_unavailable_try_again =
int(std::errc::resource_unavailable_try_again),
result_out_of_range = int(std::errc::result_out_of_range),
too_many_files_open_in_system = int(std::errc::too_many_files_open_in_system),
too_many_files_open = int(std::errc::too_many_files_open),
too_many_links = int(std::errc::too_many_links)
};
inline std::error_code make_error_code(errc E) {
return std::error_code(static_cast<int>(E), std::generic_category());
}
}
namespace std {
template <> struct is_error_code_enum<wpi::errc> : std::true_type {};
}
#endif

View File

@@ -0,0 +1,38 @@
//===- llvm/Support/Errno.h - Portable+convenient errno handling -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares some portable and convenient functions to deal with errno.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_ERRNO_H
#define WPIUTIL_WPI_ERRNO_H
#include <cerrno>
#include <string>
#include <type_traits>
namespace wpi {
namespace sys {
template <typename FailT, typename Fun, typename... Args>
inline auto RetryAfterSignal(const FailT &Fail, const Fun &F,
const Args &... As) -> decltype(F(As...)) {
decltype(F(As...)) Res;
do {
errno = 0;
Res = F(As...);
} while (Res == Fail && errno == EINTR);
return Res;
}
} // namespace sys
} // namespace wpi
#endif // WPIUTIL_WPI_ERRNO_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,144 @@
//===- llvm/Support/ErrorHandling.h - Fatal error handling ------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines an API used to indicate fatal error conditions. Non-fatal
// errors (most of them) should be handled through LLVMContext.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_ERRORHANDLING_H
#define WPIUTIL_WPI_ERRORHANDLING_H
#include "wpi/Compiler.h"
#include <string>
namespace wpi {
class StringRef;
class Twine;
/// An error handler callback.
typedef void (*fatal_error_handler_t)(void *user_data,
const std::string& reason,
bool gen_crash_diag);
/// install_fatal_error_handler - Installs a new error handler to be used
/// whenever a serious (non-recoverable) error is encountered by LLVM.
///
/// If no error handler is installed the default is to print the error message
/// to stderr, and call exit(1). If an error handler is installed then it is
/// the handler's responsibility to log the message, it will no longer be
/// printed to stderr. If the error handler returns, then exit(1) will be
/// called.
///
/// It is dangerous to naively use an error handler which throws an exception.
/// Even though some applications desire to gracefully recover from arbitrary
/// faults, blindly throwing exceptions through unfamiliar code isn't a way to
/// achieve this.
///
/// \param user_data - An argument which will be passed to the install error
/// handler.
void install_fatal_error_handler(fatal_error_handler_t handler,
void *user_data = nullptr);
/// Restores default error handling behaviour.
void remove_fatal_error_handler();
/// ScopedFatalErrorHandler - This is a simple helper class which just
/// calls install_fatal_error_handler in its constructor and
/// remove_fatal_error_handler in its destructor.
struct ScopedFatalErrorHandler {
explicit ScopedFatalErrorHandler(fatal_error_handler_t handler,
void *user_data = nullptr) {
install_fatal_error_handler(handler, user_data);
}
~ScopedFatalErrorHandler() { remove_fatal_error_handler(); }
};
/// Reports a serious error, calling any installed error handler. These
/// functions are intended to be used for error conditions which are outside
/// the control of the compiler (I/O errors, invalid user input, etc.)
///
/// If no error handler is installed the default is to print the message to
/// standard error, followed by a newline.
/// After the error handler is called this function will call exit(1), it
/// does not return.
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const char *reason,
bool gen_crash_diag = true);
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const std::string &reason,
bool gen_crash_diag = true);
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(StringRef reason,
bool gen_crash_diag = true);
LLVM_ATTRIBUTE_NORETURN void report_fatal_error(const Twine &reason,
bool gen_crash_diag = true);
/// Installs a new bad alloc error handler that should be used whenever a
/// bad alloc error, e.g. failing malloc/calloc, is encountered by LLVM.
///
/// The user can install a bad alloc handler, in order to define the behavior
/// in case of failing allocations, e.g. throwing an exception. Note that this
/// handler must not trigger any additional allocations itself.
///
/// If no error handler is installed the default is to print the error message
/// to stderr, and call exit(1). If an error handler is installed then it is
/// the handler's responsibility to log the message, it will no longer be
/// printed to stderr. If the error handler returns, then exit(1) will be
/// called.
///
///
/// \param user_data - An argument which will be passed to the installed error
/// handler.
void install_bad_alloc_error_handler(fatal_error_handler_t handler,
void *user_data = nullptr);
/// Restores default bad alloc error handling behavior.
void remove_bad_alloc_error_handler();
void install_out_of_memory_new_handler();
/// Reports a bad alloc error, calling any user defined bad alloc
/// error handler. In contrast to the generic 'report_fatal_error'
/// functions, this function is expected to return, e.g. the user
/// defined error handler throws an exception.
///
/// Note: When throwing an exception in the bad alloc handler, make sure that
/// the following unwind succeeds, e.g. do not trigger additional allocations
/// in the unwind chain.
///
/// If no error handler is installed (default), then a bad_alloc exception
/// is thrown, if LLVM is compiled with exception support, otherwise an
/// assertion is called.
void report_bad_alloc_error(const char *Reason, bool GenCrashDiag = true);
/// This function calls abort(), and prints the optional message to stderr.
/// Use the wpi_unreachable macro (that adds location info), instead of
/// calling this function directly.
LLVM_ATTRIBUTE_NORETURN void
wpi_unreachable_internal(const char *msg = nullptr, const char *file = nullptr,
unsigned line = 0);
}
/// Marks that the current location is not supposed to be reachable.
/// In !NDEBUG builds, prints the message and location info to stderr.
/// In NDEBUG builds, becomes an optimizer hint that the current location
/// is not supposed to be reachable. On compilers that don't support
/// such hints, prints a reduced message instead.
///
/// Use this instead of assert(0). It conveys intent more clearly and
/// allows compilers to omit some unnecessary code.
#ifndef NDEBUG
#define wpi_unreachable(msg) \
::wpi::wpi_unreachable_internal(msg, __FILE__, __LINE__)
#elif defined(LLVM_BUILTIN_UNREACHABLE)
#define wpi_unreachable(msg) LLVM_BUILTIN_UNREACHABLE
#else
#define wpi_unreachable(msg) ::wpi::wpi_unreachable_internal()
#endif
#endif

View File

@@ -24,18 +24,6 @@
namespace wpi {
/// Stores a reference that can be changed.
template <typename T>
class ReferenceStorage {
T *Storage;
public:
ReferenceStorage(T &Ref) : Storage(&Ref) {}
operator T &() const { return *Storage; }
T &get() const { return *Storage; }
};
/// Represents either an error or a value T.
///
/// ErrorOr<T> is a pointer-like class that represents the result of an
@@ -71,7 +59,7 @@ class ErrorOr {
static const bool isRef = std::is_reference<T>::value;
using wrap = ReferenceStorage<typename std::remove_reference<T>::type>;
using wrap = std::reference_wrapper<typename std::remove_reference<T>::type>;
public:
using storage_type = typename std::conditional<isRef, wrap, T>::type;

View File

@@ -27,9 +27,12 @@
#ifndef WPIUTIL_WPI_FILESYSTEM_H
#define WPIUTIL_WPI_FILESYSTEM_H
#include "wpi/Chrono.h"
#include "wpi/SmallString.h"
#include "wpi/StringRef.h"
#include "wpi/Twine.h"
#include "wpi/Error.h"
#include "wpi/ErrorHandling.h"
#include "wpi/ErrorOr.h"
#include <cassert>
#include <cstdint>
@@ -47,6 +50,15 @@ namespace wpi {
namespace sys {
namespace fs {
#if defined(_WIN32)
// A Win32 HANDLE is a typedef of void*
using file_t = void *;
#else
using file_t = int;
#endif
extern const file_t kInvalidFile;
/// An enumeration for the file system's view of the type.
enum class file_type {
status_error,
@@ -61,6 +73,13 @@ enum class file_type {
type_unknown
};
/// space_info - Self explanatory.
struct space_info {
uint64_t capacity;
uint64_t free;
uint64_t available;
};
enum perms {
no_perms = 0,
owner_read = 0400,
@@ -137,6 +156,8 @@ protected:
#ifndef _WIN32
time_t fs_st_atime = 0;
time_t fs_st_mtime = 0;
uint32_t fs_st_atime_nsec = 0;
uint32_t fs_st_mtime_nsec = 0;
uid_t fs_st_uid = 0;
gid_t fs_st_gid = 0;
off_t fs_st_size = 0;
@@ -157,9 +178,12 @@ public:
explicit basic_file_status(file_type Type) : Type(Type) {}
#ifndef _WIN32
basic_file_status(file_type Type, perms Perms, time_t ATime, time_t MTime,
basic_file_status(file_type Type, perms Perms, time_t ATime,
uint32_t ATimeNSec, time_t MTime, uint32_t MTimeNSec,
uid_t UID, gid_t GID, off_t Size)
: fs_st_atime(ATime), fs_st_mtime(MTime), fs_st_uid(UID), fs_st_gid(GID),
: fs_st_atime(ATime), fs_st_mtime(MTime),
fs_st_atime_nsec(ATimeNSec), fs_st_mtime_nsec(MTimeNSec),
fs_st_uid(UID), fs_st_gid(GID),
fs_st_size(Size), Type(Type), Perms(Perms) {}
#else
basic_file_status(file_type Type, perms Perms, uint32_t LastAccessTimeHigh,
@@ -177,6 +201,21 @@ public:
file_type type() const { return Type; }
perms permissions() const { return Perms; }
/// The file access time as reported from the underlying file system.
///
/// Also see comments on \c getLastModificationTime() related to the precision
/// of the returned value.
TimePoint<> getLastAccessedTime() const;
/// The file modification time as reported from the underlying file system.
///
/// The returned value allows for nanosecond precision but the actual
/// resolution is an implementation detail of the underlying file system.
/// There is no guarantee for what kind of resolution you can expect, the
/// resolution can differ across platforms and even across mountpoints on the
/// same machine.
TimePoint<> getLastModificationTime() const;
#ifndef _WIN32
uint32_t getUser() const { return fs_st_uid; }
uint32_t getGroup() const { return fs_st_gid; }
@@ -222,8 +261,11 @@ public:
#ifndef _WIN32
file_status(file_type Type, perms Perms, dev_t Dev, nlink_t Links, ino_t Ino,
time_t ATime, time_t MTime, uid_t UID, gid_t GID, off_t Size)
: basic_file_status(Type, Perms, ATime, MTime, UID, GID, Size),
time_t ATime, uint32_t ATimeNSec,
time_t MTime, uint32_t MTimeNSec,
uid_t UID, gid_t GID, off_t Size)
: basic_file_status(Type, Perms, ATime, ATimeNSec, MTime, MTimeNSec,
UID, GID, Size),
fs_st_dev(Dev), fs_st_nlinks(Links), fs_st_ino(Ino) {}
#else
file_status(file_type Type, perms Perms, uint32_t LinkCount,
@@ -256,10 +298,7 @@ public:
/// relative/../path => <current-directory>/relative/../path
///
/// @param path A path that is modified to be an absolute path.
/// @returns errc::success if \a path has been made absolute, otherwise a
/// platform-specific error_code.
std::error_code make_absolute(const Twine &current_directory,
SmallVectorImpl<char> &path);
void make_absolute(const Twine &current_directory, SmallVectorImpl<char> &path);
/// Make \a path an absolute path.
///
@@ -347,6 +386,14 @@ inline bool equivalent(const Twine &A, const Twine &B) {
return !equivalent(A, B, result) && result;
}
/// Does status represent a directory?
///
/// @param Path The path to get the type of.
/// @param Follow For symbolic links, indicates whether to return the file type
/// of the link itself, or of the target.
/// @returns A value from the file_type enumeration indicating the type of file.
file_type get_file_type(const Twine &Path, bool Follow = true);
/// Does status represent a directory?
///
/// @param status A basic_file_status previously returned from status.
@@ -462,32 +509,55 @@ bool status_known(const basic_file_status &s);
/// platform-specific error_code.
std::error_code status_known(const Twine &path, bool &result);
enum CreationDisposition : unsigned {
/// CD_CreateAlways - When opening a file:
/// * If it already exists, truncate it.
/// * If it does not already exist, create a new file.
CD_CreateAlways = 0,
/// CD_CreateNew - When opening a file:
/// * If it already exists, fail.
/// * If it does not already exist, create a new file.
CD_CreateNew = 1,
/// CD_OpenExisting - When opening a file:
/// * If it already exists, open the file with the offset set to 0.
/// * If it does not already exist, fail.
CD_OpenExisting = 2,
/// CD_OpenAlways - When opening a file:
/// * If it already exists, open the file with the offset set to 0.
/// * If it does not already exist, create a new file.
CD_OpenAlways = 3,
};
enum FileAccess : unsigned {
FA_Read = 1,
FA_Write = 2,
};
enum OpenFlags : unsigned {
F_None = 0,
/// F_Excl - When opening a file, this flag makes raw_fd_ostream
/// report an error if the file already exists.
F_Excl = 1,
/// F_Append - When opening a file, if it already exists append to the
/// existing file instead of returning an error. This may not be specified
/// with F_Excl.
F_Append = 2,
/// F_NoTrunc - When opening a file, if it already exists don't truncate
/// the file contents. F_Append implies F_NoTrunc, but F_Append seeks to
/// the end of the file, which F_NoTrunc doesn't.
F_NoTrunc = 4,
OF_None = 0,
F_None = 0, // For compatibility
/// The file should be opened in text mode on platforms that make this
/// distinction.
F_Text = 8,
OF_Text = 1,
F_Text = 1, // For compatibility
/// Open the file for read and write.
F_RW = 16,
/// The file should be opened in append mode.
OF_Append = 2,
F_Append = 2, // For compatibility
/// Delete the file on close. Only makes a difference on windows.
F_Delete = 32
OF_Delete = 4,
/// When a child process is launched, this file should remain open in the
/// child process.
OF_ChildInherit = 8,
/// Force files Atime to be updated on access. Only makes a difference on windows.
OF_UpdateAtime = 16,
};
inline OpenFlags operator|(OpenFlags A, OpenFlags B) {
@@ -499,6 +569,95 @@ inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) {
return A;
}
inline FileAccess operator|(FileAccess A, FileAccess B) {
return FileAccess(unsigned(A) | unsigned(B));
}
inline FileAccess &operator|=(FileAccess &A, FileAccess B) {
A = A | B;
return A;
}
/// @brief Opens a file with the specified creation disposition, access mode,
/// and flags and returns a file descriptor.
///
/// The caller is responsible for closing the file descriptor once they are
/// finished with it.
///
/// @param Name The path of the file to open, relative or absolute.
/// @param ResultFD If the file could be opened successfully, its descriptor
/// is stored in this location. Otherwise, this is set to -1.
/// @param Disp Value specifying the existing-file behavior.
/// @param Access Value specifying whether to open the file in read, write, or
/// read-write mode.
/// @param Flags Additional flags.
/// @param Mode The access permissions of the file, represented in octal.
/// @returns errc::success if \a Name has been opened, otherwise a
/// platform-specific error_code.
std::error_code openFile(const Twine &Name, int &ResultFD,
CreationDisposition Disp, FileAccess Access,
OpenFlags Flags, unsigned Mode = 0666);
/// @brief Opens a file with the specified creation disposition, access mode,
/// and flags and returns a platform-specific file object.
///
/// The caller is responsible for closing the file object once they are
/// finished with it.
///
/// @param Name The path of the file to open, relative or absolute.
/// @param Disp Value specifying the existing-file behavior.
/// @param Access Value specifying whether to open the file in read, write, or
/// read-write mode.
/// @param Flags Additional flags.
/// @param Mode The access permissions of the file, represented in octal.
/// @returns errc::success if \a Name has been opened, otherwise a
/// platform-specific error_code.
Expected<file_t> openNativeFile(const Twine &Name, CreationDisposition Disp,
FileAccess Access, OpenFlags Flags,
unsigned Mode = 0666);
/// @brief Opens the file with the given name in a write-only or read-write
/// mode, returning its open file descriptor. If the file does not exist, it
/// is created.
///
/// The caller is responsible for closing the file descriptor once they are
/// finished with it.
///
/// @param Name The path of the file to open, relative or absolute.
/// @param ResultFD If the file could be opened successfully, its descriptor
/// is stored in this location. Otherwise, this is set to -1.
/// @param Flags Additional flags used to determine whether the file should be
/// opened in, for example, read-write or in write-only mode.
/// @param Mode The access permissions of the file, represented in octal.
/// @returns errc::success if \a Name has been opened, otherwise a
/// platform-specific error_code.
inline std::error_code
openFileForWrite(const Twine &Name, int &ResultFD,
CreationDisposition Disp = CD_CreateAlways,
OpenFlags Flags = OF_None, unsigned Mode = 0666) {
return openFile(Name, ResultFD, Disp, FA_Write, Flags, Mode);
}
/// @brief Opens the file with the given name in a write-only or read-write
/// mode, returning its open file descriptor. If the file does not exist, it
/// is created.
///
/// The caller is responsible for closing the freeing the file once they are
/// finished with it.
///
/// @param Name The path of the file to open, relative or absolute.
/// @param Flags Additional flags used to determine whether the file should be
/// opened in, for example, read-write or in write-only mode.
/// @param Mode The access permissions of the file, represented in octal.
/// @returns a platform-specific file descriptor if \a Name has been opened,
/// otherwise an error object.
inline Expected<file_t> openNativeFileForWrite(const Twine &Name,
CreationDisposition Disp,
OpenFlags Flags,
unsigned Mode = 0666) {
return openNativeFile(Name, Disp, FA_Write, Flags, Mode);
}
/// @brief Opens the file with the given name in a write-only or read-write
/// mode, returning its open file descriptor. If the file does not exist, it
/// is created.
@@ -514,8 +673,32 @@ inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) {
/// @param Mode The access permissions of the file, represented in octal.
/// @returns errc::success if \a Name has been opened, otherwise a
/// platform-specific error_code.
std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
OpenFlags Flags, unsigned Mode = 0666);
inline std::error_code openFileForReadWrite(const Twine &Name, int &ResultFD,
CreationDisposition Disp,
OpenFlags Flags,
unsigned Mode = 0666) {
return openFile(Name, ResultFD, Disp, FA_Write | FA_Read, Flags, Mode);
}
/// @brief Opens the file with the given name in a write-only or read-write
/// mode, returning its open file descriptor. If the file does not exist, it
/// is created.
///
/// The caller is responsible for closing the freeing the file once they are
/// finished with it.
///
/// @param Name The path of the file to open, relative or absolute.
/// @param Flags Additional flags used to determine whether the file should be
/// opened in, for example, read-write or in write-only mode.
/// @param Mode The access permissions of the file, represented in octal.
/// @returns a platform-specific file descriptor if \a Name has been opened,
/// otherwise an error object.
inline Expected<file_t> openNativeFileForReadWrite(const Twine &Name,
CreationDisposition Disp,
OpenFlags Flags,
unsigned Mode = 0666) {
return openNativeFile(Name, Disp, FA_Write | FA_Read, Flags, Mode);
}
/// @brief Opens the file with the given name in a read-only mode, returning
/// its open file descriptor.
@@ -532,10 +715,79 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD,
/// @returns errc::success if \a Name has been opened, otherwise a
/// platform-specific error_code.
std::error_code openFileForRead(const Twine &Name, int &ResultFD,
OpenFlags Flags = OF_None,
SmallVectorImpl<char> *RealPath = nullptr);
/// @brief Opens the file with the given name in a read-only mode, returning
/// its open file descriptor.
///
/// The caller is responsible for closing the freeing the file once they are
/// finished with it.
///
/// @param Name The path of the file to open, relative or absolute.
/// @param RealPath If nonnull, extra work is done to determine the real path
/// of the opened file, and that path is stored in this
/// location.
/// @returns a platform-specific file descriptor if \a Name has been opened,
/// otherwise an error object.
Expected<file_t>
openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None,
SmallVectorImpl<char> *RealPath = nullptr);
/// @brief Close the file object. This should be used instead of ::close for
/// portability.
///
/// @param F On input, this is the file to close. On output, the file is
/// set to kInvalidFile.
void closeFile(file_t &F);
std::error_code getUniqueID(const Twine Path, UniqueID &Result);
/// This class represents a memory mapped file. It is based on
/// boost::iostreams::mapped_file.
class mapped_file_region {
public:
enum mapmode {
readonly, ///< May only access map via const_data as read only.
readwrite, ///< May access map via data and modify it. Written to path.
priv ///< May modify via data, but changes are lost on destruction.
};
private:
/// Platform-specific mapping state.
size_t Size;
void *Mapping;
#ifdef _WIN32
void *FileHandle;
#endif
mapmode Mode;
std::error_code init(int FD, uint64_t Offset, mapmode Mode);
public:
mapped_file_region() = delete;
mapped_file_region(mapped_file_region&) = delete;
mapped_file_region &operator =(mapped_file_region&) = delete;
/// \param fd An open file descriptor to map. mapped_file_region takes
/// ownership if closefd is true. It must have been opended in the correct
/// mode.
mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset,
std::error_code &ec);
~mapped_file_region();
size_t size() const;
char *data() const;
/// Get a const view of the data. Modifying this memory has undefined
/// behavior.
const char *const_data() const;
/// \returns The minimum alignment offset must be.
static int alignment();
};
/// @}
/// @name Iterators
/// @{
@@ -545,33 +797,37 @@ std::error_code getUniqueID(const Twine Path, UniqueID &Result);
/// called.
class directory_entry {
std::string Path;
bool FollowSymlinks;
basic_file_status Status;
file_type Type; // Most platforms can provide this.
bool FollowSymlinks; // Affects the behavior of status().
basic_file_status Status; // If available.
public:
explicit directory_entry(const Twine &path, bool follow_symlinks = true,
basic_file_status st = basic_file_status())
: Path(path.str()), FollowSymlinks(follow_symlinks), Status(st) {}
explicit directory_entry(const Twine &Path, bool FollowSymlinks = true,
file_type Type = file_type::type_unknown,
basic_file_status Status = basic_file_status())
: Path(Path.str()), Type(Type), FollowSymlinks(FollowSymlinks),
Status(Status) {}
directory_entry() = default;
void assign(const Twine &path, basic_file_status st = basic_file_status()) {
Path = path.str();
Status = st;
}
void replace_filename(const Twine &filename,
basic_file_status st = basic_file_status());
void replace_filename(const Twine &Filename, file_type Type,
basic_file_status Status = basic_file_status());
const std::string &path() const { return Path; }
ErrorOr<basic_file_status> status() const;
file_type type() const {
if (Type != file_type::type_unknown)
return Type;
auto S = status();
return S ? S->type() : file_type::type_unknown;
}
bool operator==(const directory_entry& rhs) const { return Path == rhs.Path; }
bool operator!=(const directory_entry& rhs) const { return !(*this == rhs); }
bool operator< (const directory_entry& rhs) const;
bool operator<=(const directory_entry& rhs) const;
bool operator> (const directory_entry& rhs) const;
bool operator>=(const directory_entry& rhs) const;
bool operator==(const directory_entry& RHS) const { return Path == RHS.Path; }
bool operator!=(const directory_entry& RHS) const { return !(*this == RHS); }
bool operator< (const directory_entry& RHS) const;
bool operator<=(const directory_entry& RHS) const;
bool operator> (const directory_entry& RHS) const;
bool operator>=(const directory_entry& RHS) const;
};
namespace detail {
@@ -609,7 +865,6 @@ public:
SmallString<128> path_storage;
ec = detail::directory_iterator_construct(
*State, path.toStringRef(path_storage), FollowSymlinks);
update_error_code_for_current_entry(ec);
}
explicit directory_iterator(const directory_entry &de, std::error_code &ec,
@@ -618,7 +873,6 @@ public:
State = std::make_shared<detail::DirIterState>();
ec = detail::directory_iterator_construct(
*State, de.path(), FollowSymlinks);
update_error_code_for_current_entry(ec);
}
/// Construct end iterator.
@@ -627,7 +881,6 @@ public:
// No operator++ because we need error_code.
directory_iterator &increment(std::error_code &ec) {
ec = directory_iterator_increment(*State);
update_error_code_for_current_entry(ec);
return *this;
}
@@ -647,26 +900,6 @@ public:
bool operator!=(const directory_iterator &RHS) const {
return !(*this == RHS);
}
// Other members as required by
// C++ Std, 24.1.1 Input iterators [input.iterators]
private:
// Checks if current entry is valid and populates error code. For example,
// current entry may not exist due to broken symbol links.
void update_error_code_for_current_entry(std::error_code &ec) {
// Bail out if error has already occured earlier to avoid overwriting it.
if (ec)
return;
// Empty directory entry is used to mark the end of an interation, it's not
// an error.
if (State->CurrentEntry == directory_entry())
return;
ErrorOr<basic_file_status> status = State->CurrentEntry.status();
if (!status)
ec = status.getError();
}
};
namespace detail {
@@ -704,8 +937,15 @@ public:
if (State->HasNoPushRequest)
State->HasNoPushRequest = false;
else {
ErrorOr<basic_file_status> status = State->Stack.top()->status();
if (status && is_directory(*status)) {
file_type type = State->Stack.top()->type();
if (type == file_type::symlink_file && Follow) {
// Resolve the symlink: is it a directory to recurse into?
ErrorOr<basic_file_status> status = State->Stack.top()->status();
if (status)
type = status->type();
// Otherwise broken symlink, and we'll continue.
}
if (type == file_type::directory_file) {
State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow));
if (State->Stack.top() != end_itr) {
++State->Level;
@@ -772,8 +1012,6 @@ public:
bool operator!=(const recursive_directory_iterator &RHS) const {
return !(*this == RHS);
}
// Other members as required by
// C++ Std, 24.1.1 Input iterators [input.iterators]
};
/// @}

View File

@@ -45,10 +45,12 @@
#ifndef WPIUTIL_WPI_HASHING_H
#define WPIUTIL_WPI_HASHING_H
#include "wpi/Endian.h"
#include "wpi/SwapByteOrder.h"
#include "wpi/type_traits.h"
#include <stdint.h>
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string>
#include <utility>
@@ -131,7 +133,7 @@ hash_code hash_value(const std::basic_string<T> &arg);
/// undone. This makes it thread-hostile and very hard to use outside of
/// immediately on start of a simple program designed for reproducible
/// behavior.
void set_fixed_execution_hash_seed(size_t fixed_value);
void set_fixed_execution_hash_seed(uint64_t fixed_value);
// All of the implementation details of actually computing the various hash
@@ -143,16 +145,16 @@ namespace detail {
inline uint64_t fetch64(const char *p) {
uint64_t result;
memcpy(&result, p, sizeof(result));
//if (sys::IsBigEndianHost)
// sys::swapByteOrder(result);
if (support::endian::system_endianness() == support::big)
sys::swapByteOrder(result);
return result;
}
inline uint32_t fetch32(const char *p) {
uint32_t result;
memcpy(&result, p, sizeof(result));
//if (sys::IsBigEndianHost)
// sys::swapByteOrder(result);
if (support::endian::system_endianness() == support::big)
sys::swapByteOrder(result);
return result;
}
@@ -314,9 +316,9 @@ struct hash_state {
/// This variable can be set using the \see wpi::set_fixed_execution_seed
/// function. See that function for details. Do not, under any circumstances,
/// set or read this variable.
extern size_t fixed_seed_override;
extern uint64_t fixed_seed_override;
inline size_t get_execution_seed() {
inline uint64_t get_execution_seed() {
// FIXME: This needs to be a per-execution seed. This is just a placeholder
// implementation. Switching to a per-execution seed is likely to flush out
// instability bugs and so will happen as its own commit.
@@ -324,8 +326,7 @@ inline size_t get_execution_seed() {
// However, if there is a fixed seed override set the first time this is
// called, return that instead of the per-execution seed.
const uint64_t seed_prime = 0xff51afd7ed558ccdULL;
static size_t seed = fixed_seed_override ? fixed_seed_override
: (size_t)seed_prime;
static uint64_t seed = fixed_seed_override ? fixed_seed_override : seed_prime;
return seed;
}
@@ -400,7 +401,7 @@ bool store_and_advance(char *&buffer_ptr, char *buffer_end, const T& value,
/// combining them, this (as an optimization) directly combines the integers.
template <typename InputIteratorT>
hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) {
const size_t seed = get_execution_seed();
const uint64_t seed = get_execution_seed();
char buffer[64], *buffer_ptr = buffer;
char *const buffer_end = std::end(buffer);
while (first != last && store_and_advance(buffer_ptr, buffer_end,
@@ -444,7 +445,7 @@ hash_code hash_combine_range_impl(InputIteratorT first, InputIteratorT last) {
template <typename ValueT>
typename std::enable_if<is_hashable_data<ValueT>::value, hash_code>::type
hash_combine_range_impl(ValueT *first, ValueT *last) {
const size_t seed = get_execution_seed();
const uint64_t seed = get_execution_seed();
const char *s_begin = reinterpret_cast<const char *>(first);
const char *s_end = reinterpret_cast<const char *>(last);
const size_t length = std::distance(s_begin, s_end);
@@ -494,7 +495,7 @@ namespace detail {
struct hash_combine_recursive_helper {
char buffer[64];
hash_state state;
const size_t seed;
const uint64_t seed;
public:
/// Construct a recursive hash combining helper.

View File

@@ -0,0 +1,97 @@
//===-- llvm/Support/ManagedStatic.h - Static Global wrapper ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the ManagedStatic class and the wpi_shutdown() function.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_MANAGEDSTATIC_H
#define WPIUTIL_WPI_MANAGEDSTATIC_H
#include <atomic>
#include <cstddef>
namespace wpi {
/// object_creator - Helper method for ManagedStatic.
template <class C> struct object_creator {
static void *call() { return new C(); }
};
/// object_deleter - Helper method for ManagedStatic.
///
template <typename T> struct object_deleter {
static void call(void *Ptr) { delete (T *)Ptr; }
};
template <typename T, size_t N> struct object_deleter<T[N]> {
static void call(void *Ptr) { delete[](T *)Ptr; }
};
/// ManagedStaticBase - Common base class for ManagedStatic instances.
class ManagedStaticBase {
protected:
// This should only be used as a static variable, which guarantees that this
// will be zero initialized.
mutable std::atomic<void *> Ptr;
mutable void (*DeleterFn)(void*);
mutable const ManagedStaticBase *Next;
void RegisterManagedStatic(void *(*creator)(), void (*deleter)(void*)) const;
public:
/// isConstructed - Return true if this object has not been created yet.
bool isConstructed() const { return Ptr != nullptr; }
void destroy() const;
};
/// ManagedStatic - This transparently changes the behavior of global statics to
/// be lazily constructed on demand (good for reducing startup times of dynamic
/// libraries that link in LLVM components) and for making destruction be
/// explicit through the wpi_shutdown() function call.
///
template <class C, class Creator = object_creator<C>,
class Deleter = object_deleter<C>>
class ManagedStatic : public ManagedStaticBase {
public:
// Accessors.
C &operator*() {
void *Tmp = Ptr.load(std::memory_order_acquire);
if (!Tmp)
RegisterManagedStatic(Creator::call, Deleter::call);
return *static_cast<C *>(Ptr.load(std::memory_order_relaxed));
}
C *operator->() { return &**this; }
const C &operator*() const {
void *Tmp = Ptr.load(std::memory_order_acquire);
if (!Tmp)
RegisterManagedStatic(Creator::call, Deleter::call);
return *static_cast<C *>(Ptr.load(std::memory_order_relaxed));
}
const C *operator->() const { return &**this; }
};
/// wpi_shutdown - Deallocate and destroy all ManagedStatic variables.
void wpi_shutdown();
/// wpi_shutdown_obj - This is a simple helper class that calls
/// wpi_shutdown() when it is destroyed.
struct wpi_shutdown_obj {
wpi_shutdown_obj() = default;
~wpi_shutdown_obj() { wpi_shutdown(); }
};
} // end namespace wpi
#endif // WPIUTIL_WPI_MANAGEDSTATIC_H

View File

@@ -25,7 +25,15 @@
#include <type_traits>
#ifdef _MSC_VER
#include <intrin.h>
// Declare these intrinsics manually rather including intrin.h. It's very
// expensive, and MathExtras.h is popular.
// #include <intrin.h>
extern "C" {
unsigned char _BitScanForward(unsigned long *_Index, unsigned long _Mask);
unsigned char _BitScanForward64(unsigned long *_Index, unsigned __int64 _Mask);
unsigned char _BitScanReverse(unsigned long *_Index, unsigned long _Mask);
unsigned char _BitScanReverse64(unsigned long *_Index, unsigned __int64 _Mask);
}
#endif
namespace wpi {

View File

@@ -0,0 +1,49 @@
//===- MemAlloc.h - Memory allocation functions -----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines counterparts of C library allocation functions defined in
/// the namespace 'std'. The new allocation functions crash on allocation
/// failure instead of returning null pointer.
///
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_MEMALLOC_H
#define WPIUTIL_WPI_MEMALLOC_H
#include "wpi/Compiler.h"
#include "wpi/ErrorHandling.h"
#include <cstdlib>
namespace wpi {
LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_malloc(size_t Sz) {
void *Result = std::malloc(Sz);
if (Result == nullptr)
report_bad_alloc_error("Allocation failed");
return Result;
}
LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_calloc(size_t Count,
size_t Sz) {
void *Result = std::calloc(Count, Sz);
if (Result == nullptr)
report_bad_alloc_error("Allocation failed");
return Result;
}
LLVM_ATTRIBUTE_RETURNS_NONNULL inline void *safe_realloc(void *Ptr, size_t Sz) {
void *Result = std::realloc(Ptr, Sz);
if (Result == nullptr)
report_bad_alloc_error("Allocation failed");
return Result;
}
}
#endif

View File

@@ -20,6 +20,7 @@
#include "wpi/iterator.h"
#include <cstdint>
#include <iterator>
#include <system_error>
namespace wpi {
namespace sys {
@@ -360,22 +361,6 @@ void system_temp_directory(bool erasedOnReboot, SmallVectorImpl<char> &result);
/// @result True if a home directory is set, false otherwise.
bool home_directory(SmallVectorImpl<char> &result);
/// Get the user's cache directory.
///
/// Expect the resulting path to be a directory shared with other
/// applications/services used by the user. Params \p Path1 to \p Path3 can be
/// used to append additional directory names to the resulting path. Recommended
/// pattern is <user_cache_directory>/<vendor>/<application>.
///
/// @param Result Holds the resulting path.
/// @param Path1 Additional path to be appended to the user's cache directory
/// path. "" can be used to append nothing.
/// @param Path2 Second additional path to be appended.
/// @param Path3 Third additional path to be appended.
/// @result True if a cache directory path is set, false otherwise.
bool user_cache_directory(SmallVectorImpl<char> &Result, const Twine &Path1,
const Twine &Path2 = "", const Twine &Path3 = "");
/// Has root name?
///
/// root_name != ""
@@ -467,6 +452,10 @@ StringRef remove_leading_dotslash(StringRef path, Style style = Style::native);
bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot = false,
Style style = Style::native);
#if defined(_WIN32)
std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16);
#endif
} // end namespace path
} // end namespace sys
} // end namespace wpi

View File

@@ -112,6 +112,39 @@ template <> struct PointerLikeTypeTraits<uintptr_t> {
enum { NumLowBitsAvailable = 0 };
};
/// Provide suitable custom traits struct for function pointers.
///
/// Function pointers can't be directly given these traits as functions can't
/// have their alignment computed with `alignof` and we need different casting.
///
/// To rely on higher alignment for a specialized use, you can provide a
/// customized form of this template explicitly with higher alignment, and
/// potentially use alignment attributes on functions to satisfy that.
template <int Alignment, typename FunctionPointerT>
struct FunctionPointerLikeTypeTraits {
enum { NumLowBitsAvailable = detail::ConstantLog2<Alignment>::value };
static inline void *getAsVoidPointer(FunctionPointerT P) {
assert((reinterpret_cast<uintptr_t>(P) &
~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 &&
"Alignment not satisfied for an actual function pointer!");
return reinterpret_cast<void *>(P);
}
static inline FunctionPointerT getFromVoidPointer(void *P) {
return reinterpret_cast<FunctionPointerT>(P);
}
};
/// Provide a default specialization for function pointers that assumes 4-byte
/// alignment.
///
/// We assume here that functions used with this are always at least 4-byte
/// aligned. This means that, for example, thumb functions won't work or systems
/// with weird unaligned function pointers won't work. But all practical systems
/// we support satisfy this requirement.
template <typename ReturnT, typename... ParamTs>
struct PointerLikeTypeTraits<ReturnT (*)(ParamTs...)>
: FunctionPointerLikeTypeTraits<4, ReturnT (*)(ParamTs...)> {};
} // end namespace wpi
#endif

View File

@@ -21,6 +21,7 @@
#include "wpi/iterator.h"
#include "wpi/iterator_range.h"
#include "wpi/optional.h"
#include "wpi/ErrorHandling.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
@@ -52,6 +53,29 @@ using ValueOfRange = typename std::remove_reference<decltype(
} // end namespace detail
//===----------------------------------------------------------------------===//
// Extra additions to <type_traits>
//===----------------------------------------------------------------------===//
template <typename T>
struct negation : std::integral_constant<bool, !bool(T::value)> {};
template <typename...> struct conjunction : std::true_type {};
template <typename B1> struct conjunction<B1> : B1 {};
template <typename B1, typename... Bn>
struct conjunction<B1, Bn...>
: std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
template <typename T> struct make_const_ptr {
using type =
typename std::add_pointer<typename std::add_const<T>::type>::type;
};
template <typename T> struct make_const_ref {
using type = typename std::add_lvalue_reference<
typename std::add_const<T>::type>::type;
};
//===----------------------------------------------------------------------===//
// Extra additions to <functional>
//===----------------------------------------------------------------------===//
@@ -176,6 +200,12 @@ void adl_swap(T &&lhs, T &&rhs) noexcept(
adl_detail::adl_swap(std::forward<T>(lhs), std::forward<T>(rhs));
}
/// Test whether \p RangeOrContainer is empty. Similar to C++17 std::empty.
template <typename T>
constexpr bool empty(const T &RangeOrContainer) {
return adl_begin(RangeOrContainer) == adl_end(RangeOrContainer);
}
// mapped_iterator - This is a simple iterator adapter that causes a function to
// be applied whenever operator* is invoked on the iterator.
@@ -403,6 +433,8 @@ make_filter_range(RangeT &&Range, PredicateT Pred) {
// forward declarations required by zip_shortest/zip_first
template <typename R, typename UnaryPredicate>
bool all_of(R &&range, UnaryPredicate P);
template <typename R, typename UnaryPredicate>
bool any_of(R &&range, UnaryPredicate P);
template <size_t... I> struct index_sequence;
@@ -553,6 +585,132 @@ detail::zippy<detail::zip_first, T, U, Args...> zip_first(T &&t, U &&u,
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
}
namespace detail {
template <typename Iter>
static Iter next_or_end(const Iter &I, const Iter &End) {
if (I == End)
return End;
return std::next(I);
}
template <typename Iter>
static auto deref_or_none(const Iter &I, const Iter &End)
-> wpi::optional<typename std::remove_const<
typename std::remove_reference<decltype(*I)>::type>::type> {
if (I == End)
return nullopt;
return *I;
}
template <typename Iter> struct ZipLongestItemType {
using type =
wpi::optional<typename std::remove_const<typename std::remove_reference<
decltype(*std::declval<Iter>())>::type>::type>;
};
template <typename... Iters> struct ZipLongestTupleType {
using type = std::tuple<typename ZipLongestItemType<Iters>::type...>;
};
template <typename... Iters>
class zip_longest_iterator
: public iterator_facade_base<
zip_longest_iterator<Iters...>,
typename std::common_type<
std::forward_iterator_tag,
typename std::iterator_traits<Iters>::iterator_category...>::type,
typename ZipLongestTupleType<Iters...>::type,
typename std::iterator_traits<typename std::tuple_element<
0, std::tuple<Iters...>>::type>::difference_type,
typename ZipLongestTupleType<Iters...>::type *,
typename ZipLongestTupleType<Iters...>::type> {
public:
using value_type = typename ZipLongestTupleType<Iters...>::type;
private:
std::tuple<Iters...> iterators;
std::tuple<Iters...> end_iterators;
template <size_t... Ns>
bool test(const zip_longest_iterator<Iters...> &other,
index_sequence<Ns...>) const {
return wpi::any_of(
std::initializer_list<bool>{std::get<Ns>(this->iterators) !=
std::get<Ns>(other.iterators)...},
identity<bool>{});
}
template <size_t... Ns> value_type deref(index_sequence<Ns...>) const {
return value_type(
deref_or_none(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
}
template <size_t... Ns>
decltype(iterators) tup_inc(index_sequence<Ns...>) const {
return std::tuple<Iters...>(
next_or_end(std::get<Ns>(iterators), std::get<Ns>(end_iterators))...);
}
public:
zip_longest_iterator(std::pair<Iters &&, Iters &&>... ts)
: iterators(std::forward<Iters>(ts.first)...),
end_iterators(std::forward<Iters>(ts.second)...) {}
value_type operator*() { return deref(index_sequence_for<Iters...>{}); }
value_type operator*() const { return deref(index_sequence_for<Iters...>{}); }
zip_longest_iterator<Iters...> &operator++() {
iterators = tup_inc(index_sequence_for<Iters...>{});
return *this;
}
bool operator==(const zip_longest_iterator<Iters...> &other) const {
return !test(other, index_sequence_for<Iters...>{});
}
};
template <typename... Args> class zip_longest_range {
public:
using iterator =
zip_longest_iterator<decltype(adl_begin(std::declval<Args>()))...>;
using iterator_category = typename iterator::iterator_category;
using value_type = typename iterator::value_type;
using difference_type = typename iterator::difference_type;
using pointer = typename iterator::pointer;
using reference = typename iterator::reference;
private:
std::tuple<Args...> ts;
template <size_t... Ns> iterator begin_impl(index_sequence<Ns...>) const {
return iterator(std::make_pair(adl_begin(std::get<Ns>(ts)),
adl_end(std::get<Ns>(ts)))...);
}
template <size_t... Ns> iterator end_impl(index_sequence<Ns...>) const {
return iterator(std::make_pair(adl_end(std::get<Ns>(ts)),
adl_end(std::get<Ns>(ts)))...);
}
public:
zip_longest_range(Args &&... ts_) : ts(std::forward<Args>(ts_)...) {}
iterator begin() const { return begin_impl(index_sequence_for<Args...>{}); }
iterator end() const { return end_impl(index_sequence_for<Args...>{}); }
};
} // namespace detail
/// Iterate over two or more iterators at the same time. Iteration continues
/// until all iterators reach the end. The wpi::optional only contains a value
/// if the iterator has not reached the end.
template <typename T, typename U, typename... Args>
detail::zip_longest_range<T, U, Args...> zip_longest(T &&t, U &&u,
Args &&... args) {
return detail::zip_longest_range<T, U, Args...>(
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
}
/// Iterator wrapper that concatenates sequences together.
///
/// This can concatenate different iterators, even with different types, into
@@ -575,18 +733,20 @@ class concat_iterator
/// Note that something like iterator_range seems nice at first here, but the
/// range properties are of little benefit and end up getting in the way
/// because we need to do mutation on the current iterators.
std::tuple<std::pair<IterTs, IterTs>...> IterPairs;
std::tuple<IterTs...> Begins;
std::tuple<IterTs...> Ends;
/// Attempts to increment a specific iterator.
///
/// Returns true if it was able to increment the iterator. Returns false if
/// the iterator is already at the end iterator.
template <size_t Index> bool incrementHelper() {
auto &IterPair = std::get<Index>(IterPairs);
if (IterPair.first == IterPair.second)
auto &Begin = std::get<Index>(Begins);
auto &End = std::get<Index>(Ends);
if (Begin == End)
return false;
++IterPair.first;
++Begin;
return true;
}
@@ -610,11 +770,12 @@ class concat_iterator
/// dereferences the iterator and returns the address of the resulting
/// reference.
template <size_t Index> ValueT *getHelper() const {
auto &IterPair = std::get<Index>(IterPairs);
if (IterPair.first == IterPair.second)
auto &Begin = std::get<Index>(Begins);
auto &End = std::get<Index>(Ends);
if (Begin == End)
return nullptr;
return &*IterPair.first;
return &*Begin;
}
/// Finds the first non-end iterator, dereferences, and returns the resulting
@@ -641,7 +802,7 @@ public:
/// iterators.
template <typename... RangeTs>
explicit concat_iterator(RangeTs &&... Ranges)
: IterPairs({std::begin(Ranges), std::end(Ranges)}...) {}
: Begins(std::begin(Ranges)...), Ends(std::end(Ranges)...) {}
using BaseT::operator++;
@@ -653,7 +814,7 @@ public:
ValueT &operator*() const { return get(index_sequence_for<IterTs...>()); }
bool operator==(const concat_iterator &RHS) const {
return IterPairs == RHS.IterPairs;
return Begins == RHS.Begins && Ends == RHS.Ends;
}
};
@@ -722,6 +883,19 @@ struct less_second {
}
};
/// \brief Function object to apply a binary function to the first component of
/// a std::pair.
template<typename FuncTy>
struct on_first {
FuncTy func;
template <typename T>
auto operator()(const T &lhs, const T &rhs) const
-> decltype(func(lhs.first, rhs.first)) {
return func(lhs.first, rhs.first);
}
};
// A subset of N3658. More stuff can be added as-needed.
/// Represents a compile-time sequence of integers.
@@ -862,6 +1036,18 @@ void DeleteContainerSeconds(Container &C) {
C.clear();
}
/// Get the size of a range. This is a wrapper function around std::distance
/// which is only enabled when the operation is O(1).
template <typename R>
auto size(R &&Range, typename std::enable_if<
std::is_same<typename std::iterator_traits<decltype(
Range.begin())>::iterator_category,
std::random_access_iterator_tag>::value,
void>::type * = nullptr)
-> decltype(std::distance(Range.begin(), Range.end())) {
return std::distance(Range.begin(), Range.end());
}
/// Provide wrappers to std::for_each which take ranges instead of having to
/// pass begin/end explicitly.
template <typename R, typename UnaryPredicate>
@@ -972,6 +1158,33 @@ auto lower_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) {
return std::lower_bound(adl_begin(Range), adl_end(Range), I);
}
template <typename R, typename ForwardIt, typename Compare>
auto lower_bound(R &&Range, ForwardIt I, Compare C)
-> decltype(adl_begin(Range)) {
return std::lower_bound(adl_begin(Range), adl_end(Range), I, C);
}
/// Provide wrappers to std::upper_bound which take ranges instead of having to
/// pass begin/end explicitly.
template <typename R, typename ForwardIt>
auto upper_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) {
return std::upper_bound(adl_begin(Range), adl_end(Range), I);
}
template <typename R, typename ForwardIt, typename Compare>
auto upper_bound(R &&Range, ForwardIt I, Compare C)
-> decltype(adl_begin(Range)) {
return std::upper_bound(adl_begin(Range), adl_end(Range), I, C);
}
/// Wrapper function around std::equal to detect if all elements
/// in a container are same.
template <typename R>
bool is_splat(R &&Range) {
size_t range_size = size(Range);
return range_size != 0 && (range_size == 1 ||
std::equal(adl_begin(Range) + 1, adl_end(Range), adl_begin(Range)));
}
/// Given a range of type R, iterate the entire range and return a
/// SmallVector with elements of the vector. This is useful, for example,
/// when you want to iterate a range and then sort the results.
@@ -993,18 +1206,6 @@ void erase_if(Container &C, UnaryPredicate P) {
C.erase(remove_if(C, P), C.end());
}
/// Get the size of a range. This is a wrapper function around std::distance
/// which is only enabled when the operation is O(1).
template <typename R>
auto size(R &&Range, typename std::enable_if<
std::is_same<typename std::iterator_traits<decltype(
Range.begin())>::iterator_category,
std::random_access_iterator_tag>::value,
void>::type * = nullptr)
-> decltype(std::distance(Range.begin(), Range.end())) {
return std::distance(Range.begin(), Range.end());
}
//===----------------------------------------------------------------------===//
// Extra additions to <memory>
//===----------------------------------------------------------------------===//
@@ -1217,6 +1418,40 @@ auto apply_tuple(F &&f, Tuple &&t) -> decltype(detail::apply_tuple_impl(
Indices{});
}
/// Return true if the sequence [Begin, End) has exactly N items. Runs in O(N)
/// time. Not meant for use with random-access iterators.
template <typename IterTy>
bool hasNItems(
IterTy &&Begin, IterTy &&End, unsigned N,
typename std::enable_if<
!std::is_same<
typename std::iterator_traits<typename std::remove_reference<
decltype(Begin)>::type>::iterator_category,
std::random_access_iterator_tag>::value,
void>::type * = nullptr) {
for (; N; --N, ++Begin)
if (Begin == End)
return false; // Too few.
return Begin == End;
}
/// Return true if the sequence [Begin, End) has N or more items. Runs in O(N)
/// time. Not meant for use with random-access iterators.
template <typename IterTy>
bool hasNItemsOrMore(
IterTy &&Begin, IterTy &&End, unsigned N,
typename std::enable_if<
!std::is_same<
typename std::iterator_traits<typename std::remove_reference<
decltype(Begin)>::type>::iterator_category,
std::random_access_iterator_tag>::value,
void>::type * = nullptr) {
for (; N; --N, ++Begin)
if (Begin == End)
return false; // Too few.
return true;
}
} // end namespace wpi
#endif // LLVM_ADT_STLEXTRAS_H

View File

@@ -18,20 +18,119 @@
#include "wpi/SmallPtrSet.h"
#include "wpi/SmallVector.h"
#include "wpi/Compiler.h"
#include "wpi/iterator.h"
#include "wpi/type_traits.h"
#include <cstddef>
#include <functional>
#include <set>
#include <type_traits>
#include <utility>
namespace wpi {
/// SmallSetIterator - This class implements a const_iterator for SmallSet by
/// delegating to the underlying SmallVector or Set iterators.
template <typename T, unsigned N, typename C>
class SmallSetIterator
: public iterator_facade_base<SmallSetIterator<T, N, C>,
std::forward_iterator_tag, T> {
private:
using SetIterTy = typename std::set<T, C>::const_iterator;
using VecIterTy = typename SmallVector<T, N>::const_iterator;
using SelfTy = SmallSetIterator<T, N, C>;
/// Iterators to the parts of the SmallSet containing the data. They are set
/// depending on isSmall.
union {
SetIterTy SetIter;
VecIterTy VecIter;
};
bool isSmall;
public:
SmallSetIterator(SetIterTy SetIter) : SetIter(SetIter), isSmall(false) {}
SmallSetIterator(VecIterTy VecIter) : VecIter(VecIter), isSmall(true) {}
// Spell out destructor, copy/move constructor and assignment operators for
// MSVC STL, where set<T>::const_iterator is not trivially copy constructible.
~SmallSetIterator() {
if (isSmall)
VecIter.~VecIterTy();
else
SetIter.~SetIterTy();
}
SmallSetIterator(const SmallSetIterator &Other) : isSmall(Other.isSmall) {
if (isSmall)
VecIter = Other.VecIter;
else
// Use placement new, to make sure SetIter is properly constructed, even
// if it is not trivially copy-able (e.g. in MSVC).
new (&SetIter) SetIterTy(Other.SetIter);
}
SmallSetIterator(SmallSetIterator &&Other) : isSmall(Other.isSmall) {
if (isSmall)
VecIter = std::move(Other.VecIter);
else
// Use placement new, to make sure SetIter is properly constructed, even
// if it is not trivially copy-able (e.g. in MSVC).
new (&SetIter) SetIterTy(std::move(Other.SetIter));
}
SmallSetIterator& operator=(const SmallSetIterator& Other) {
// Call destructor for SetIter, so it gets properly destroyed if it is
// not trivially destructible in case we are setting VecIter.
if (!isSmall)
SetIter.~SetIterTy();
isSmall = Other.isSmall;
if (isSmall)
VecIter = Other.VecIter;
else
new (&SetIter) SetIterTy(Other.SetIter);
return *this;
}
SmallSetIterator& operator=(SmallSetIterator&& Other) {
// Call destructor for SetIter, so it gets properly destroyed if it is
// not trivially destructible in case we are setting VecIter.
if (!isSmall)
SetIter.~SetIterTy();
isSmall = Other.isSmall;
if (isSmall)
VecIter = std::move(Other.VecIter);
else
new (&SetIter) SetIterTy(std::move(Other.SetIter));
return *this;
}
bool operator==(const SmallSetIterator &RHS) const {
if (isSmall != RHS.isSmall)
return false;
if (isSmall)
return VecIter == RHS.VecIter;
return SetIter == RHS.SetIter;
}
SmallSetIterator &operator++() { // Preincrement
if (isSmall)
VecIter++;
else
SetIter++;
return *this;
}
const T &operator*() const { return isSmall ? *VecIter : *SetIter; }
};
/// SmallSet - This maintains a set of unique values, optimizing for the case
/// when the set is small (less than N). In this case, the set can be
/// maintained with no mallocs. If the set gets large, we expand to using an
/// std::set to maintain reasonable lookup times.
///
/// Note that this set does not provide a way to iterate over members in the
/// set.
template <typename T, unsigned N, typename C = std::less<T>>
class SmallSet {
/// Use a SmallVector to hold the elements here (even though it will never
@@ -50,6 +149,7 @@ class SmallSet {
public:
using size_type = size_t;
using const_iterator = SmallSetIterator<T, N, C>;
SmallSet() = default;
@@ -121,6 +221,18 @@ public:
Set.clear();
}
const_iterator begin() const {
if (isSmall())
return {Vector.begin()};
return {Set.begin()};
}
const_iterator end() const {
if (isSmall())
return {Vector.end()};
return {Set.end()};
}
private:
bool isSmall() const { return Set.empty(); }

View File

@@ -26,7 +26,7 @@
#include "wpi/AlignOf.h"
#include "wpi/Compiler.h"
#include "wpi/MathExtras.h"
#include "wpi/memory.h"
#include "wpi/MemAlloc.h"
#include "wpi/type_traits.h"
#include <algorithm>
#include <cassert>
@@ -45,28 +45,44 @@ namespace wpi {
/// This is all the non-templated stuff common to all SmallVectors.
class SmallVectorBase {
protected:
void *BeginX, *EndX, *CapacityX;
void *BeginX;
unsigned Size = 0, Capacity;
protected:
SmallVectorBase(void *FirstEl, size_t Size)
: BeginX(FirstEl), EndX(FirstEl), CapacityX((char*)FirstEl+Size) {}
SmallVectorBase() = delete;
SmallVectorBase(void *FirstEl, size_t Capacity)
: BeginX(FirstEl), Capacity(Capacity) {}
/// This is an implementation of the grow() method which only works
/// on POD-like data types and is out of line to reduce code duplication.
void grow_pod(void *FirstEl, size_t MinSizeInBytes, size_t TSize);
void grow_pod(void *FirstEl, size_t MinCapacity, size_t TSize);
public:
/// This returns size()*sizeof(T).
size_t size_in_bytes() const {
return size_t((char*)EndX - (char*)BeginX);
}
LLVM_ATTRIBUTE_ALWAYS_INLINE
size_t size() const { return Size; }
LLVM_ATTRIBUTE_ALWAYS_INLINE
size_t capacity() const { return Capacity; }
/// capacity_in_bytes - This returns capacity()*sizeof(T).
size_t capacity_in_bytes() const {
return size_t((char*)CapacityX - (char*)BeginX);
}
LLVM_NODISCARD bool empty() const { return !Size; }
LLVM_NODISCARD bool empty() const { return BeginX == EndX; }
/// Set the array size to \p N, which the current array must have enough
/// capacity for.
///
/// This does not construct or destroy any elements in the vector.
///
/// Clients can use this in conjunction with capacity() to write past the end
/// of the buffer when they know that more elements are available, and only
/// update the size later. This avoids the cost of value initializing elements
/// which will only be overwritten.
void set_size(size_t Size) {
assert(Size <= capacity());
this->Size = Size;
}
};
/// Figure out the offset of the first element.
template <class T, typename = void> struct SmallVectorAlignmentAndSize {
AlignedCharArrayUnion<SmallVectorBase> Base;
AlignedCharArrayUnion<T> FirstEl;
};
/// This is the part of SmallVectorTemplateBase which does not depend on whether
@@ -74,36 +90,34 @@ public:
/// to avoid unnecessarily requiring T to be complete.
template <typename T, typename = void>
class SmallVectorTemplateCommon : public SmallVectorBase {
private:
template <typename, unsigned> friend struct SmallVectorStorage;
// Allocate raw space for N elements of type T. If T has a ctor or dtor, we
// don't want it to be automatically run, so we need to represent the space as
// something else. Use an array of char of sufficient alignment.
using U = AlignedCharArrayUnion<T>;
U FirstEl;
/// Find the address of the first element. For this pointer math to be valid
/// with small-size of 0 for T with lots of alignment, it's important that
/// SmallVectorStorage is properly-aligned even for small-size of 0.
void *getFirstEl() const {
return const_cast<void *>(reinterpret_cast<const void *>(
reinterpret_cast<const char *>(this) +
offsetof(SmallVectorAlignmentAndSize<T>, FirstEl)));
}
// Space after 'FirstEl' is clobbered, do not add any instance vars after it.
protected:
SmallVectorTemplateCommon(size_t Size) : SmallVectorBase(&FirstEl, Size) {}
SmallVectorTemplateCommon(size_t Size)
: SmallVectorBase(getFirstEl(), Size) {}
void grow_pod(size_t MinSizeInBytes, size_t TSize) {
SmallVectorBase::grow_pod(&FirstEl, MinSizeInBytes, TSize);
void grow_pod(size_t MinCapacity, size_t TSize) {
SmallVectorBase::grow_pod(getFirstEl(), MinCapacity, TSize);
}
/// Return true if this is a smallvector which has not had dynamic
/// memory allocated for it.
bool isSmall() const {
return BeginX == static_cast<const void*>(&FirstEl);
}
bool isSmall() const { return BeginX == getFirstEl(); }
/// Put this vector in a state of being small.
void resetToSmall() {
BeginX = EndX = CapacityX = &FirstEl;
BeginX = getFirstEl();
Size = Capacity = 0; // FIXME: Setting Capacity to 0 is suspect.
}
void setEnd(T *P) { this->EndX = P; }
public:
using size_type = size_t;
using difference_type = ptrdiff_t;
@@ -125,27 +139,20 @@ public:
LLVM_ATTRIBUTE_ALWAYS_INLINE
const_iterator begin() const { return (const_iterator)this->BeginX; }
LLVM_ATTRIBUTE_ALWAYS_INLINE
iterator end() { return (iterator)this->EndX; }
iterator end() { return begin() + size(); }
LLVM_ATTRIBUTE_ALWAYS_INLINE
const_iterator end() const { return (const_iterator)this->EndX; }
const_iterator end() const { return begin() + size(); }
protected:
iterator capacity_ptr() { return (iterator)this->CapacityX; }
const_iterator capacity_ptr() const { return (const_iterator)this->CapacityX;}
public:
// reverse iterator creation methods.
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const{ return const_reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const { return const_reverse_iterator(begin());}
LLVM_ATTRIBUTE_ALWAYS_INLINE
size_type size() const { return end()-begin(); }
size_type size_in_bytes() const { return size() * sizeof(T); }
size_type max_size() const { return size_type(-1) / sizeof(T); }
/// Return the total number of elements in the currently allocated buffer.
size_t capacity() const { return capacity_ptr() - begin(); }
size_t capacity_in_bytes() const { return capacity() * sizeof(T); }
/// Return a pointer to the vector's buffer, even if empty().
pointer data() { return pointer(begin()); }
@@ -184,7 +191,7 @@ public:
/// SmallVectorTemplateBase<isPodLike = false> - This is where we put method
/// implementations that are designed to work with non-POD-like T's.
template <typename T, bool isPodLike>
template <typename T, bool = isPodLike<T>::value>
class SmallVectorTemplateBase : public SmallVectorTemplateCommon<T> {
protected:
SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon<T>(Size) {}
@@ -218,21 +225,21 @@ protected:
public:
void push_back(const T &Elt) {
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
this->grow();
::new ((void*) this->end()) T(Elt);
this->setEnd(this->end()+1);
this->set_size(this->size() + 1);
}
void push_back(T &&Elt) {
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
this->grow();
::new ((void*) this->end()) T(::std::move(Elt));
this->setEnd(this->end()+1);
this->set_size(this->size() + 1);
}
void pop_back() {
this->setEnd(this->end()-1);
this->set_size(this->size() - 1);
this->end()->~T();
}
};
@@ -240,13 +247,13 @@ public:
// Define this out-of-line to dissuade the C++ compiler from inlining it.
template <typename T, bool isPodLike>
void SmallVectorTemplateBase<T, isPodLike>::grow(size_t MinSize) {
size_t CurCapacity = this->capacity();
size_t CurSize = this->size();
if (MinSize > UINT32_MAX)
report_bad_alloc_error("SmallVector capacity overflow during allocation");
// Always grow, even from zero.
size_t NewCapacity = size_t(NextPowerOf2(CurCapacity+2));
if (NewCapacity < MinSize)
NewCapacity = MinSize;
T *NewElts = static_cast<T*>(CheckedMalloc(NewCapacity*sizeof(T)));
size_t NewCapacity = size_t(NextPowerOf2(this->capacity() + 2));
NewCapacity = std::min(std::max(NewCapacity, MinSize), size_t(UINT32_MAX));
T *NewElts = static_cast<T*>(wpi::safe_malloc(NewCapacity*sizeof(T)));
// Move the elements over.
this->uninitialized_move(this->begin(), this->end(), NewElts);
@@ -258,9 +265,8 @@ void SmallVectorTemplateBase<T, isPodLike>::grow(size_t MinSize) {
if (!this->isSmall())
free(this->begin());
this->setEnd(NewElts+CurSize);
this->BeginX = NewElts;
this->CapacityX = this->begin()+NewCapacity;
this->Capacity = NewCapacity;
}
@@ -302,33 +308,29 @@ protected:
// use memcpy here. Note that I and E are iterators and thus might be
// invalid for memcpy if they are equal.
if (I != E)
memcpy(Dest, I, (E - I) * sizeof(T));
memcpy(reinterpret_cast<void *>(Dest), I, (E - I) * sizeof(T));
}
/// Double the size of the allocated memory, guaranteeing space for at
/// least one more element or MinSize if specified.
void grow(size_t MinSize = 0) {
this->grow_pod(MinSize*sizeof(T), sizeof(T));
}
void grow(size_t MinSize = 0) { this->grow_pod(MinSize, sizeof(T)); }
public:
void push_back(const T &Elt) {
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
this->grow();
memcpy(this->end(), &Elt, sizeof(T));
this->setEnd(this->end()+1);
memcpy(reinterpret_cast<void *>(this->end()), &Elt, sizeof(T));
this->set_size(this->size() + 1);
}
void pop_back() {
this->setEnd(this->end()-1);
}
void pop_back() { this->set_size(this->size() - 1); }
};
/// This class consists of common code factored out of the SmallVector class to
/// reduce code duplication based on the SmallVector 'N' template parameter.
template <typename T>
class SmallVectorImpl : public SmallVectorTemplateBase<T, isPodLike<T>::value> {
using SuperClass = SmallVectorTemplateBase<T, isPodLike<T>::value>;
class SmallVectorImpl : public SmallVectorTemplateBase<T> {
using SuperClass = SmallVectorTemplateBase<T>;
public:
using iterator = typename SuperClass::iterator;
@@ -338,8 +340,7 @@ public:
protected:
// Default ctor - Initialize to empty.
explicit SmallVectorImpl(unsigned N)
: SmallVectorTemplateBase<T, isPodLike<T>::value>(N*sizeof(T)) {
}
: SmallVectorTemplateBase<T, isPodLike<T>::value>(N) {}
public:
SmallVectorImpl(const SmallVectorImpl &) = delete;
@@ -353,31 +354,31 @@ public:
void clear() {
this->destroy_range(this->begin(), this->end());
this->EndX = this->BeginX;
this->Size = 0;
}
void resize(size_type N) {
if (N < this->size()) {
this->destroy_range(this->begin()+N, this->end());
this->setEnd(this->begin()+N);
this->set_size(N);
} else if (N > this->size()) {
if (this->capacity() < N)
this->grow(N);
for (auto I = this->end(), E = this->begin() + N; I != E; ++I)
new (&*I) T();
this->setEnd(this->begin()+N);
this->set_size(N);
}
}
void resize(size_type N, const T &NV) {
if (N < this->size()) {
this->destroy_range(this->begin()+N, this->end());
this->setEnd(this->begin()+N);
this->set_size(N);
} else if (N > this->size()) {
if (this->capacity() < N)
this->grow(N);
std::uninitialized_fill(this->end(), this->begin()+N, NV);
this->setEnd(this->begin()+N);
this->set_size(N);
}
}
@@ -402,23 +403,23 @@ public:
void append(in_iter in_start, in_iter in_end) {
size_type NumInputs = std::distance(in_start, in_end);
// Grow allocated space if needed.
if (NumInputs > size_type(this->capacity_ptr()-this->end()))
if (NumInputs > this->capacity() - this->size())
this->grow(this->size()+NumInputs);
// Copy the new elements over.
this->uninitialized_copy(in_start, in_end, this->end());
this->setEnd(this->end() + NumInputs);
this->set_size(this->size() + NumInputs);
}
/// Add the specified range to the end of the SmallVector.
void append(size_type NumInputs, const T &Elt) {
// Grow allocated space if needed.
if (NumInputs > size_type(this->capacity_ptr()-this->end()))
if (NumInputs > this->capacity() - this->size())
this->grow(this->size()+NumInputs);
// Copy the new elements over.
std::uninitialized_fill_n(this->end(), NumInputs, Elt);
this->setEnd(this->end() + NumInputs);
this->set_size(this->size() + NumInputs);
}
void append(std::initializer_list<T> IL) {
@@ -432,7 +433,7 @@ public:
clear();
if (this->capacity() < NumElts)
this->grow(NumElts);
this->setEnd(this->begin()+NumElts);
this->set_size(NumElts);
std::uninitialized_fill(this->begin(), this->end(), Elt);
}
@@ -479,7 +480,7 @@ public:
iterator I = std::move(E, this->end(), S);
// Drop the last elts.
this->destroy_range(I, this->end());
this->setEnd(I);
this->set_size(I - this->begin());
return(N);
}
@@ -492,7 +493,7 @@ public:
assert(I >= this->begin() && "Insertion iterator is out of bounds.");
assert(I <= this->end() && "Inserting past the end of the vector.");
if (this->EndX >= this->CapacityX) {
if (this->size() >= this->capacity()) {
size_t EltNo = I-this->begin();
this->grow();
I = this->begin()+EltNo;
@@ -501,12 +502,12 @@ public:
::new ((void*) this->end()) T(::std::move(this->back()));
// Push everything else over.
std::move_backward(I, this->end()-1, this->end());
this->setEnd(this->end()+1);
this->set_size(this->size() + 1);
// If we just moved the element we're inserting, be sure to update
// the reference.
T *EltPtr = &Elt;
if (I <= EltPtr && EltPtr < this->EndX)
if (I <= EltPtr && EltPtr < this->end())
++EltPtr;
*I = ::std::move(*EltPtr);
@@ -522,7 +523,7 @@ public:
assert(I >= this->begin() && "Insertion iterator is out of bounds.");
assert(I <= this->end() && "Inserting past the end of the vector.");
if (this->EndX >= this->CapacityX) {
if (this->size() >= this->capacity()) {
size_t EltNo = I-this->begin();
this->grow();
I = this->begin()+EltNo;
@@ -530,12 +531,12 @@ public:
::new ((void*) this->end()) T(std::move(this->back()));
// Push everything else over.
std::move_backward(I, this->end()-1, this->end());
this->setEnd(this->end()+1);
this->set_size(this->size() + 1);
// If we just moved the element we're inserting, be sure to update
// the reference.
const T *EltPtr = &Elt;
if (I <= EltPtr && EltPtr < this->EndX)
if (I <= EltPtr && EltPtr < this->end())
++EltPtr;
*I = *EltPtr;
@@ -581,7 +582,7 @@ public:
// Move over the elements that we're about to overwrite.
T *OldEnd = this->end();
this->setEnd(this->end() + NumToInsert);
this->set_size(this->size() + NumToInsert);
size_t NumOverwritten = OldEnd-I;
this->uninitialized_move(I, OldEnd, this->end()-NumOverwritten);
@@ -638,7 +639,7 @@ public:
// Move over the elements that we're about to overwrite.
T *OldEnd = this->end();
this->setEnd(this->end() + NumToInsert);
this->set_size(this->size() + NumToInsert);
size_t NumOverwritten = OldEnd-I;
this->uninitialized_move(I, OldEnd, this->end()-NumOverwritten);
@@ -658,10 +659,10 @@ public:
}
template <typename... ArgTypes> void emplace_back(ArgTypes &&... Args) {
if (LLVM_UNLIKELY(this->EndX >= this->CapacityX))
if (LLVM_UNLIKELY(this->size() >= this->capacity()))
this->grow();
::new ((void *)this->end()) T(std::forward<ArgTypes>(Args)...);
this->setEnd(this->end() + 1);
this->set_size(this->size() + 1);
}
SmallVectorImpl &operator=(const SmallVectorImpl &RHS);
@@ -680,20 +681,6 @@ public:
return std::lexicographical_compare(this->begin(), this->end(),
RHS.begin(), RHS.end());
}
/// Set the array size to \p N, which the current array must have enough
/// capacity for.
///
/// This does not construct or destroy any elements in the vector.
///
/// Clients can use this in conjunction with capacity() to write past the end
/// of the buffer when they know that more elements are available, and only
/// update the size later. This avoids the cost of value initializing elements
/// which will only be overwritten.
void set_size(size_type N) {
assert(N <= this->capacity());
this->setEnd(this->begin() + N);
}
};
template <typename T>
@@ -703,8 +690,8 @@ void SmallVectorImpl<T>::swap(SmallVectorImpl<T> &RHS) {
// We can only avoid copying elements if neither vector is small.
if (!this->isSmall() && !RHS.isSmall()) {
std::swap(this->BeginX, RHS.BeginX);
std::swap(this->EndX, RHS.EndX);
std::swap(this->CapacityX, RHS.CapacityX);
std::swap(this->Size, RHS.Size);
std::swap(this->Capacity, RHS.Capacity);
return;
}
if (RHS.size() > this->capacity())
@@ -722,15 +709,15 @@ void SmallVectorImpl<T>::swap(SmallVectorImpl<T> &RHS) {
if (this->size() > RHS.size()) {
size_t EltDiff = this->size() - RHS.size();
this->uninitialized_copy(this->begin()+NumShared, this->end(), RHS.end());
RHS.setEnd(RHS.end()+EltDiff);
RHS.set_size(RHS.size() + EltDiff);
this->destroy_range(this->begin()+NumShared, this->end());
this->setEnd(this->begin()+NumShared);
this->set_size(NumShared);
} else if (RHS.size() > this->size()) {
size_t EltDiff = RHS.size() - this->size();
this->uninitialized_copy(RHS.begin()+NumShared, RHS.end(), this->end());
this->setEnd(this->end() + EltDiff);
this->set_size(this->size() + EltDiff);
this->destroy_range(RHS.begin()+NumShared, RHS.end());
RHS.setEnd(RHS.begin()+NumShared);
RHS.set_size(NumShared);
}
}
@@ -756,7 +743,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::
this->destroy_range(NewEnd, this->end());
// Trim.
this->setEnd(NewEnd);
this->set_size(RHSSize);
return *this;
}
@@ -766,7 +753,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::
if (this->capacity() < RHSSize) {
// Destroy current elements.
this->destroy_range(this->begin(), this->end());
this->setEnd(this->begin());
this->set_size(0);
CurSize = 0;
this->grow(RHSSize);
} else if (CurSize) {
@@ -779,7 +766,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::
this->begin()+CurSize);
// Set end.
this->setEnd(this->begin()+RHSSize);
this->set_size(RHSSize);
return *this;
}
@@ -793,8 +780,8 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
this->destroy_range(this->begin(), this->end());
if (!this->isSmall()) free(this->begin());
this->BeginX = RHS.BeginX;
this->EndX = RHS.EndX;
this->CapacityX = RHS.CapacityX;
this->Size = RHS.Size;
this->Capacity = RHS.Capacity;
RHS.resetToSmall();
return *this;
}
@@ -811,7 +798,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
// Destroy excess elements and trim the bounds.
this->destroy_range(NewEnd, this->end());
this->setEnd(NewEnd);
this->set_size(RHSSize);
// Clear the RHS.
RHS.clear();
@@ -826,7 +813,7 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
if (this->capacity() < RHSSize) {
// Destroy current elements.
this->destroy_range(this->begin(), this->end());
this->setEnd(this->begin());
this->set_size(0);
CurSize = 0;
this->grow(RHSSize);
} else if (CurSize) {
@@ -839,22 +826,23 @@ SmallVectorImpl<T> &SmallVectorImpl<T>::operator=(SmallVectorImpl<T> &&RHS) {
this->begin()+CurSize);
// Set end.
this->setEnd(this->begin()+RHSSize);
this->set_size(RHSSize);
RHS.clear();
return *this;
}
/// Storage for the SmallVector elements which aren't contained in
/// SmallVectorTemplateCommon. There are 'N-1' elements here. The remaining '1'
/// element is in the base class. This is specialized for the N=1 and N=0 cases
/// Storage for the SmallVector elements. This is specialized for the N=0 case
/// to avoid allocating unnecessary storage.
template <typename T, unsigned N>
struct SmallVectorStorage {
typename SmallVectorTemplateCommon<T>::U InlineElts[N - 1];
AlignedCharArrayUnion<T> InlineElts[N];
};
template <typename T> struct SmallVectorStorage<T, 1> {};
template <typename T> struct SmallVectorStorage<T, 0> {};
/// We need the storage to be properly aligned even for small-size of 0 so that
/// the pointer math in \a SmallVectorTemplateCommon::getFirstEl() is
/// well-defined.
template <typename T> struct alignas(alignof(T)) SmallVectorStorage<T, 0> {};
/// This is a 'vector' (really, a variable-sized array), optimized
/// for the case when the array is small. It contains some number of elements
@@ -865,10 +853,7 @@ template <typename T> struct SmallVectorStorage<T, 0> {};
/// Note that this does not attempt to be exception safe.
///
template <typename T, unsigned N>
class SmallVector : public SmallVectorImpl<T> {
/// Inline space for elements which aren't stored in the base class.
SmallVectorStorage<T, N> Storage;
class SmallVector : public SmallVectorImpl<T>, SmallVectorStorage<T, N> {
public:
SmallVector() : SmallVectorImpl<T>(N) {}

View File

@@ -78,6 +78,26 @@ inline bool isAlpha(char C) {
/// lowercase letter as classified by "C" locale.
inline bool isAlnum(char C) { return isAlpha(C) || isDigit(C); }
/// Checks whether character \p C is valid ASCII (high bit is zero).
inline bool isASCII(char C) { return static_cast<unsigned char>(C) <= 127; }
/// Checks whether all characters in S are ASCII.
inline bool isASCII(wpi::StringRef S) {
for (char C : S)
if (LLVM_UNLIKELY(!isASCII(C)))
return false;
return true;
}
/// Checks whether character \p C is printable.
///
/// Locale-independent version of the C standard library isprint whose results
/// may differ on different platforms.
inline bool isPrint(char C) {
unsigned char UC = static_cast<unsigned char>(C);
return (0x20 <= UC) && (UC <= 0x7E);
}
/// Returns the corresponding lowercase character if \p x is uppercase.
inline char toLower(char x) {
if (x >= 'A' && x <= 'Z')
@@ -109,22 +129,23 @@ inline std::string utohexstr(uint64_t X, bool LowerCase = false) {
/// Convert buffer \p Input to its hexadecimal representation.
/// The returned string is double the size of \p Input.
inline std::string toHex(StringRef Input) {
inline std::string toHex(StringRef Input, bool LowerCase = false) {
static const char *const LUT = "0123456789ABCDEF";
const uint8_t Offset = LowerCase ? 32 : 0;
size_t Length = Input.size();
std::string Output;
Output.reserve(2 * Length);
for (size_t i = 0; i < Length; ++i) {
const unsigned char c = Input[i];
Output.push_back(LUT[c >> 4]);
Output.push_back(LUT[c & 15]);
Output.push_back(LUT[c >> 4] | Offset);
Output.push_back(LUT[c & 15] | Offset);
}
return Output;
}
inline std::string toHex(ArrayRef<uint8_t> Input) {
return toHex(toStringRef(Input));
inline std::string toHex(ArrayRef<uint8_t> Input, bool LowerCase = false) {
return toHex(toStringRef(Input), LowerCase);
}
inline uint8_t hexFromNibbles(char MSB, char LSB) {
@@ -264,9 +285,13 @@ inline StringRef getOrdinalSuffix(unsigned Val) {
}
}
/// PrintEscapedString - Print each character of the specified string, escaping
/// it if it is not printable or if it is an escape char.
void PrintEscapedString(StringRef Name, raw_ostream &Out);
/// Print each character of the specified string, escaping it if it is not
/// printable or if it is an escape char.
void printEscapedString(StringRef Name, raw_ostream &Out);
/// Print each character of the specified string, escaping HTML special
/// characters.
void printHTMLEscaped(StringRef String, raw_ostream &Out);
/// printLowerCase - Print each character as lowercase if it is uppercase.
void printLowerCase(StringRef String, raw_ostream &Out);
@@ -377,4 +402,4 @@ inline std::string join_items(Sep Separator, Args &&... Items) {
} // end namespace wpi
#endif // LLVM_ADT_STRINGEXTRAS_H
#endif // WPIUTIL_WPI_STRINGEXTRAS_H

View File

@@ -18,9 +18,10 @@
#include "wpi/StringRef.h"
#include "wpi/iterator.h"
#include "wpi/iterator_range.h"
#include "wpi/MemAlloc.h"
#include "wpi/PointerLikeTypeTraits.h"
#include "wpi/ErrorHandling.h"
#include "wpi/deprecated.h"
#include "wpi/memory.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
@@ -163,7 +164,7 @@ public:
size_t AllocSize = sizeof(StringMapEntry) + KeyLength + 1;
StringMapEntry *NewItem =
static_cast<StringMapEntry*>(CheckedMalloc(AllocSize));
static_cast<StringMapEntry*>(safe_malloc(AllocSize));
// Construct the value.
new (NewItem) StringMapEntry(KeyLength, std::forward<InitTy>(InitVals)...);
@@ -358,12 +359,6 @@ public:
return try_emplace(KV.first, std::move(KV.second));
}
template <typename... ArgsTy>
WPI_DEPRECATED("use try_emplace instead")
std::pair<iterator, bool> emplace_second(StringRef Key, ArgsTy &&... Args) {
return try_emplace(Key, std::forward<ArgsTy>(Args)...);
}
/// Emplace a new element for the specified key into the map if the key isn't
/// already in the map. The bool component of the returned pair is true
/// if and only if the insertion takes place, and the iterator component of

View File

@@ -130,7 +130,7 @@ namespace wpi {
/// empty - Check if the string is empty.
LLVM_NODISCARD
LLVM_ATTRIBUTE_ALWAYS_INLINE
bool empty() const noexcept { return size() == 0; }
bool empty() const noexcept { return Length == 0; }
/// size - Get the string size.
LLVM_NODISCARD
@@ -683,10 +683,7 @@ namespace wpi {
/// \returns The split substrings.
LLVM_NODISCARD
std::pair<StringRef, StringRef> split(char Separator) const {
size_t Idx = find(Separator);
if (Idx == npos)
return std::make_pair(*this, StringRef());
return std::make_pair(slice(0, Idx), slice(Idx+1, npos));
return split(StringRef(&Separator, 1));
}
/// Split into two substrings around the first occurrence of a separator
@@ -707,6 +704,24 @@ namespace wpi {
return std::make_pair(slice(0, Idx), slice(Idx + Separator.size(), npos));
}
/// Split into two substrings around the last occurrence of a separator
/// string.
///
/// If \p Separator is in the string, then the result is a pair (LHS, RHS)
/// such that (*this == LHS + Separator + RHS) is true and RHS is
/// minimal. If \p Separator is not in the string, then the result is a
/// pair (LHS, RHS) where (*this == LHS) and (RHS == "").
///
/// \param Separator - The string to split on.
/// \return - The split substrings.
LLVM_NODISCARD
std::pair<StringRef, StringRef> rsplit(StringRef Separator) const {
size_t Idx = rfind(Separator);
if (Idx == npos)
return std::make_pair(*this, StringRef());
return std::make_pair(slice(0, Idx), slice(Idx + Separator.size(), npos));
}
/// Split into substrings around the occurrences of a separator string.
///
/// Each substring is stored in \p A. If \p MaxSplit is >= 0, at most
@@ -754,10 +769,7 @@ namespace wpi {
/// \return - The split substrings.
LLVM_NODISCARD
std::pair<StringRef, StringRef> rsplit(char Separator) const {
size_t Idx = rfind(Separator);
if (Idx == npos)
return std::make_pair(*this, StringRef());
return std::make_pair(slice(0, Idx), slice(Idx+1, npos));
return rsplit(StringRef(&Separator, 1));
}
/// Return string with consecutive \p Char characters starting from the

View File

@@ -0,0 +1,127 @@
//===- SwapByteOrder.h - Generic and optimized byte swaps -------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares generic and optimized functions to swap the byte order of
// an integral type.
//
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_SWAPBYTEORDER_H
#define WPIUTIL_WPI_SWAPBYTEORDER_H
#include "wpi/Compiler.h"
#include <cstddef>
#include <stdint.h>
#if defined(_MSC_VER) && !defined(_DEBUG)
#include <stdlib.h>
#endif
namespace wpi {
namespace sys {
/// SwapByteOrder_16 - This function returns a byte-swapped representation of
/// the 16-bit argument.
inline uint16_t SwapByteOrder_16(uint16_t value) {
#if defined(_MSC_VER) && !defined(_DEBUG)
// The DLL version of the runtime lacks these functions (bug!?), but in a
// release build they're replaced with BSWAP instructions anyway.
return _byteswap_ushort(value);
#else
uint16_t Hi = value << 8;
uint16_t Lo = value >> 8;
return Hi | Lo;
#endif
}
/// SwapByteOrder_32 - This function returns a byte-swapped representation of
/// the 32-bit argument.
inline uint32_t SwapByteOrder_32(uint32_t value) {
#if defined(__llvm__) || (LLVM_GNUC_PREREQ(4, 3, 0) && !defined(__ICC))
return __builtin_bswap32(value);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_ulong(value);
#else
uint32_t Byte0 = value & 0x000000FF;
uint32_t Byte1 = value & 0x0000FF00;
uint32_t Byte2 = value & 0x00FF0000;
uint32_t Byte3 = value & 0xFF000000;
return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
#endif
}
/// SwapByteOrder_64 - This function returns a byte-swapped representation of
/// the 64-bit argument.
inline uint64_t SwapByteOrder_64(uint64_t value) {
#if defined(__llvm__) || (LLVM_GNUC_PREREQ(4, 3, 0) && !defined(__ICC))
return __builtin_bswap64(value);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_uint64(value);
#else
uint64_t Hi = SwapByteOrder_32(uint32_t(value));
uint32_t Lo = SwapByteOrder_32(uint32_t(value >> 32));
return (Hi << 32) | Lo;
#endif
}
inline unsigned char getSwappedBytes(unsigned char C) { return C; }
inline signed char getSwappedBytes(signed char C) { return C; }
inline char getSwappedBytes(char C) { return C; }
inline unsigned short getSwappedBytes(unsigned short C) { return SwapByteOrder_16(C); }
inline signed short getSwappedBytes( signed short C) { return SwapByteOrder_16(C); }
inline unsigned int getSwappedBytes(unsigned int C) { return SwapByteOrder_32(C); }
inline signed int getSwappedBytes( signed int C) { return SwapByteOrder_32(C); }
#if __LONG_MAX__ == __INT_MAX__
inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_32(C); }
inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_32(C); }
#elif __LONG_MAX__ == __LONG_LONG_MAX__
inline unsigned long getSwappedBytes(unsigned long C) { return SwapByteOrder_64(C); }
inline signed long getSwappedBytes( signed long C) { return SwapByteOrder_64(C); }
#else
#error "Unknown long size!"
#endif
inline unsigned long long getSwappedBytes(unsigned long long C) {
return SwapByteOrder_64(C);
}
inline signed long long getSwappedBytes(signed long long C) {
return SwapByteOrder_64(C);
}
inline float getSwappedBytes(float C) {
union {
uint32_t i;
float f;
} in, out;
in.f = C;
out.i = SwapByteOrder_32(in.i);
return out.f;
}
inline double getSwappedBytes(double C) {
union {
uint64_t i;
double d;
} in, out;
in.d = C;
out.i = SwapByteOrder_64(in.i);
return out.d;
}
template<typename T>
inline void swapByteOrder(T &Value) {
Value = getSwappedBytes(Value);
}
} // end namespace sys
} // end namespace wpi
#endif

View File

@@ -12,6 +12,7 @@
#include "wpi/SmallVector.h"
#include "wpi/StringRef.h"
#include "wpi/ErrorHandling.h"
#include <cassert>
#include <cstdint>
#include <string>

View File

@@ -0,0 +1,154 @@
//===- VersionTuple.h - Version Number Handling -----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// Defines the llvm::VersionTuple class, which represents a version in
/// the form major[.minor[.subminor]].
///
//===----------------------------------------------------------------------===//
#ifndef WPIUTIL_WPI_VERSIONTUPLE_H
#define WPIUTIL_WPI_VERSIONTUPLE_H
#include "wpi/StringRef.h"
#include "wpi/optional.h"
#include "wpi/raw_ostream.h"
#include <string>
#include <tuple>
namespace wpi {
/// Represents a version number in the form major[.minor[.subminor[.build]]].
class VersionTuple {
unsigned Major : 32;
unsigned Minor : 31;
unsigned HasMinor : 1;
unsigned Subminor : 31;
unsigned HasSubminor : 1;
unsigned Build : 31;
unsigned HasBuild : 1;
public:
VersionTuple()
: Major(0), Minor(0), HasMinor(false), Subminor(0), HasSubminor(false),
Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major)
: Major(Major), Minor(0), HasMinor(false), Subminor(0),
HasSubminor(false), Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major, unsigned Minor)
: Major(Major), Minor(Minor), HasMinor(true), Subminor(0),
HasSubminor(false), Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor)
: Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
HasSubminor(true), Build(0), HasBuild(false) {}
explicit VersionTuple(unsigned Major, unsigned Minor, unsigned Subminor,
unsigned Build)
: Major(Major), Minor(Minor), HasMinor(true), Subminor(Subminor),
HasSubminor(true), Build(Build), HasBuild(true) {}
/// Determine whether this version information is empty
/// (e.g., all version components are zero).
bool empty() const {
return Major == 0 && Minor == 0 && Subminor == 0 && Build == 0;
}
/// Retrieve the major version number.
unsigned getMajor() const { return Major; }
/// Retrieve the minor version number, if provided.
optional<unsigned> getMinor() const {
if (!HasMinor)
return nullopt;
return Minor;
}
/// Retrieve the subminor version number, if provided.
optional<unsigned> getSubminor() const {
if (!HasSubminor)
return nullopt;
return Subminor;
}
/// Retrieve the build version number, if provided.
optional<unsigned> getBuild() const {
if (!HasBuild)
return nullopt;
return Build;
}
/// Determine if two version numbers are equivalent. If not
/// provided, minor and subminor version numbers are considered to be zero.
friend bool operator==(const VersionTuple &X, const VersionTuple &Y) {
return X.Major == Y.Major && X.Minor == Y.Minor &&
X.Subminor == Y.Subminor && X.Build == Y.Build;
}
/// Determine if two version numbers are not equivalent.
///
/// If not provided, minor and subminor version numbers are considered to be
/// zero.
friend bool operator!=(const VersionTuple &X, const VersionTuple &Y) {
return !(X == Y);
}
/// Determine whether one version number precedes another.
///
/// If not provided, minor and subminor version numbers are considered to be
/// zero.
friend bool operator<(const VersionTuple &X, const VersionTuple &Y) {
return std::tie(X.Major, X.Minor, X.Subminor, X.Build) <
std::tie(Y.Major, Y.Minor, Y.Subminor, Y.Build);
}
/// Determine whether one version number follows another.
///
/// If not provided, minor and subminor version numbers are considered to be
/// zero.
friend bool operator>(const VersionTuple &X, const VersionTuple &Y) {
return Y < X;
}
/// Determine whether one version number precedes or is
/// equivalent to another.
///
/// If not provided, minor and subminor version numbers are considered to be
/// zero.
friend bool operator<=(const VersionTuple &X, const VersionTuple &Y) {
return !(Y < X);
}
/// Determine whether one version number follows or is
/// equivalent to another.
///
/// If not provided, minor and subminor version numbers are considered to be
/// zero.
friend bool operator>=(const VersionTuple &X, const VersionTuple &Y) {
return !(X < Y);
}
/// Retrieve a string representation of the version number.
std::string getAsString() const;
/// Try to parse the given string as a version number.
/// \returns \c true if the string does not match the regular expression
/// [0-9]+(\.[0-9]+){0,3}
bool tryParse(StringRef string);
};
/// Print a version number.
raw_ostream &operator<<(raw_ostream &Out, const VersionTuple &V);
} // end namespace wpi
#endif // WPIUTIL_WPI_VERSIONTUPLE_H

View File

@@ -202,9 +202,7 @@ template <
typename ReferenceT = typename std::conditional<
std::is_same<T, typename std::iterator_traits<
WrappedIteratorT>::value_type>::value,
typename std::iterator_traits<WrappedIteratorT>::reference, T &>::type,
// Don't provide these, they are mostly to act as aliases below.
typename WrappedTraitsT = std::iterator_traits<WrappedIteratorT>>
typename std::iterator_traits<WrappedIteratorT>::reference, T &>::type>
class iterator_adaptor_base
: public iterator_facade_base<DerivedT, IteratorCategoryT, T,
DifferenceTypeT, PointerT, ReferenceT> {
@@ -288,7 +286,7 @@ template <typename WrappedIteratorT,
decltype(**std::declval<WrappedIteratorT>())>::type>
struct pointee_iterator
: iterator_adaptor_base<
pointee_iterator<WrappedIteratorT>, WrappedIteratorT,
pointee_iterator<WrappedIteratorT, T>, WrappedIteratorT,
typename std::iterator_traits<WrappedIteratorT>::iterator_category,
T> {
pointee_iterator() = default;
@@ -311,8 +309,10 @@ make_pointee_range(RangeT &&Range) {
template <typename WrappedIteratorT,
typename T = decltype(&*std::declval<WrappedIteratorT>())>
class pointer_iterator
: public iterator_adaptor_base<pointer_iterator<WrappedIteratorT>,
WrappedIteratorT, T> {
: public iterator_adaptor_base<
pointer_iterator<WrappedIteratorT, T>, WrappedIteratorT,
typename std::iterator_traits<WrappedIteratorT>::iterator_category,
T> {
mutable T Ptr;
public:

View File

@@ -59,9 +59,10 @@ template <typename T> iterator_range<T> make_range(std::pair<T, T> p) {
return iterator_range<T>(std::move(p.first), std::move(p.second));
}
template<typename T>
iterator_range<decltype(begin(std::declval<T>()))> drop_begin(T &&t, int n) {
return make_range(std::next(begin(t), n), end(t));
template <typename T>
iterator_range<decltype(adl_begin(std::declval<T>()))> drop_begin(T &&t,
int n) {
return make_range(std::next(adl_begin(t), n), adl_end(t));
}
}

View File

@@ -1,40 +0,0 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
#ifndef WPIUTIL_WPI_MEMORY_H_
#define WPIUTIL_WPI_MEMORY_H_
#include <cstdlib>
namespace wpi {
/**
* Wrapper around std::calloc that calls std::terminate on out of memory.
* @param num number of objects to allocate
* @param size number of bytes per object to allocate
* @return Pointer to beginning of newly allocated memory.
*/
void* CheckedCalloc(size_t num, size_t size);
/**
* Wrapper around std::malloc that calls std::terminate on out of memory.
* @param size number of bytes to allocate
* @return Pointer to beginning of newly allocated memory.
*/
void* CheckedMalloc(size_t size);
/**
* Wrapper around std::realloc that calls std::terminate on out of memory.
* @param ptr memory previously allocated
* @param size number of bytes to allocate
* @return Pointer to beginning of newly allocated memory.
*/
void* CheckedRealloc(void* ptr, size_t size);
} // namespace wpi
#endif // WPIUTIL_WPI_MEMORY_H_

View File

@@ -34,7 +34,9 @@ class FormattedBytes;
namespace sys {
namespace fs {
enum FileAccess : unsigned;
enum OpenFlags : unsigned;
enum CreationDisposition : unsigned;
} // end namespace fs
} // end namespace sys
@@ -239,7 +241,7 @@ public:
raw_ostream &write_hex(unsigned long long N);
/// Output \p Str, turning '\\', '\t', '\n', '"', and anything that doesn't
/// satisfy std::isprint into an escape sequence.
/// satisfy wpi::isPrint into an escape sequence.
raw_ostream &write_escaped(StringRef Str, bool UseHexEscapes = false);
raw_ostream &write(unsigned char C);
@@ -389,12 +391,18 @@ class raw_fd_ostream : public raw_pwrite_stream {
int FD;
bool ShouldClose;
bool SupportsSeeking;
#ifdef _WIN32
/// True if this fd refers to a Windows console device. Mintty and other
/// terminal emulators are TTYs, but they are not consoles.
bool IsWindowsConsole = false;
#endif
std::error_code EC;
uint64_t pos;
bool SupportsSeeking;
/// See raw_ostream::write_impl.
void write_impl(const char *Ptr, size_t Size) override;
@@ -419,15 +427,22 @@ public:
/// \p Flags allows optional flags to control how the file will be opened.
///
/// As a special case, if Filename is "-", then the stream will use
/// STDOUT_FILENO instead of opening a file. Note that it will still consider
/// itself to own the file descriptor. In particular, it will close the
/// file descriptor when it is done (this is necessary to detect
/// output errors).
/// STDOUT_FILENO instead of opening a file. This will not close the stdout
/// descriptor.
raw_fd_ostream(StringRef Filename, std::error_code &EC);
raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::CreationDisposition Disp);
raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::FileAccess Access);
raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::OpenFlags Flags);
raw_fd_ostream(StringRef Filename, std::error_code &EC,
sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access,
sys::fs::OpenFlags Flags);
/// FD is the file descriptor that this writes to. If ShouldClose is true,
/// this closes the file when the stream is destroyed.
/// this closes the file when the stream is destroyed. If FD is for stdout or
/// stderr, it will not be closed.
raw_fd_ostream(int fd, bool shouldClose, bool unbuffered=false);
~raw_fd_ostream() override;
@@ -653,6 +668,8 @@ class buffer_ostream : public raw_svector_ostream {
raw_ostream &OS;
SmallVector<char, 0> Buffer;
virtual void anchor() override;
public:
buffer_ostream(raw_ostream &OS) : raw_svector_ostream(Buffer), OS(OS) {}
~buffer_ostream() override { OS << str(); }