diff --git a/cscore/src/main/native/cpp/HttpCameraImpl.cpp b/cscore/src/main/native/cpp/HttpCameraImpl.cpp index 296c3422c0..f2699b8fba 100644 --- a/cscore/src/main/native/cpp/HttpCameraImpl.cpp +++ b/cscore/src/main/native/cpp/HttpCameraImpl.cpp @@ -7,9 +7,9 @@ #include "HttpCameraImpl.h" +#include #include #include -#include #include #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(wpi::CheckedMalloc(urls.size() * sizeof(char*))); + static_cast(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; diff --git a/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp b/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp index 082e3982d5..09b798fc9a 100644 --- a/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp +++ b/cscore/src/main/native/cpp/UsbCameraImplCommon.cpp @@ -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( - 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( - 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( - 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; diff --git a/cscore/src/main/native/cpp/c_util.h b/cscore/src/main/native/cpp/c_util.h index 6264e1fc71..8a2a456af9 100644 --- a/cscore/src/main/native/cpp/c_util.h +++ b/cscore/src/main/native/cpp/c_util.h @@ -11,13 +11,13 @@ #include #include +#include #include -#include namespace cs { inline char* ConvertToC(wpi::StringRef in) { - char* out = static_cast(wpi::CheckedMalloc(in.size() + 1)); + char* out = static_cast(wpi::safe_malloc(in.size() + 1)); std::memmove(out, in.data(), in.size()); out[in.size()] = '\0'; return out; diff --git a/cscore/src/main/native/cpp/cscore_c.cpp b/cscore/src/main/native/cpp/cscore_c.cpp index 1819d13835..35216263d6 100644 --- a/cscore/src/main/native/cpp/cscore_c.cpp +++ b/cscore/src/main/native/cpp/cscore_c.cpp @@ -11,8 +11,8 @@ #include #include +#include #include -#include #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(wpi::CheckedMalloc(choices.size() * sizeof(char*))); + static_cast(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 buf; auto vec = cs::EnumerateSourceProperties(source, buf, status); CS_Property* out = static_cast( - 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( - 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 buf; auto handles = cs::EnumerateSourceSinks(source, buf, status); - CS_Sink* sinks = static_cast( - wpi::CheckedMalloc(handles.size() * sizeof(CS_Sink))); + CS_Sink* sinks = + static_cast(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 buf; auto vec = cs::EnumerateSinkProperties(sink, buf, status); CS_Property* out = static_cast( - 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 buf; auto handles = cs::EnumerateSourceHandles(buf, status); CS_Source* sources = static_cast( - 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 buf; auto handles = cs::EnumerateSinkHandles(buf, status); - CS_Sink* sinks = static_cast( - wpi::CheckedMalloc(handles.size() * sizeof(CS_Sink))); + CS_Sink* sinks = + static_cast(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( - wpi::CheckedMalloc(interfaces.size() * sizeof(char*))); + char** out = + static_cast(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]); diff --git a/cscore/src/main/native/linux/UsbCameraImpl.cpp b/cscore/src/main/native/linux/UsbCameraImpl.cpp index 125c9369f8..780a1056b2 100644 --- a/cscore/src/main/native/linux/UsbCameraImpl.cpp +++ b/cscore/src/main/native/linux/UsbCameraImpl.cpp @@ -25,9 +25,9 @@ #include #include +#include #include #include -#include #include #include diff --git a/cscore/src/main/native/windows/UsbCameraImpl.cpp b/cscore/src/main/native/windows/UsbCameraImpl.cpp index 3a6446554f..5d2777e053 100644 --- a/cscore/src/main/native/windows/UsbCameraImpl.cpp +++ b/cscore/src/main/native/windows/UsbCameraImpl.cpp @@ -28,8 +28,8 @@ #include #include #include +#include #include -#include #include #include diff --git a/ntcore/src/main/native/cpp/Value.cpp b/ntcore/src/main/native/cpp/Value.cpp index 61390f033c..500db21264 100644 --- a/ntcore/src/main/native/cpp/Value.cpp +++ b/ntcore/src/main/native/cpp/Value.cpp @@ -7,7 +7,7 @@ #include -#include +#include #include #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(wpi::CheckedMalloc(v.size() * sizeof(int))); + static_cast(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(wpi::CheckedMalloc(v.size() * sizeof(double))); + static_cast(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( - 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(wpi::CheckedMalloc(in.size() + 1)); + out->str = static_cast(wpi::safe_malloc(in.size() + 1)); std::memcpy(out->str, in.data(), in.size()); out->str[in.size()] = '\0'; } diff --git a/ntcore/src/main/native/cpp/WireDecoder.cpp b/ntcore/src/main/native/cpp/WireDecoder.cpp index 132e8a2a04..f175d6d136 100644 --- a/ntcore/src/main/native/cpp/WireDecoder.cpp +++ b/ntcore/src/main/native/cpp/WireDecoder.cpp @@ -14,8 +14,8 @@ #include #include +#include #include -#include 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(wpi::CheckedMalloc(m_allocated)); + m_buf = static_cast(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(wpi::CheckedRealloc(m_buf, newlen)); + m_buf = static_cast(wpi::safe_realloc(m_buf, newlen)); m_allocated = newlen; } diff --git a/ntcore/src/main/native/cpp/ntcore_c.cpp b/ntcore/src/main/native/cpp/ntcore_c.cpp index 6c71e55c6c..14be7d429b 100644 --- a/ntcore/src/main/native/cpp/ntcore_c.cpp +++ b/ntcore/src/main/native/cpp/ntcore_c.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include "Value_internal.h" @@ -21,7 +21,7 @@ using namespace nt; // Conversion helpers static void ConvertToC(wpi::StringRef in, char** out) { - *out = static_cast(wpi::CheckedMalloc(in.size() + 1)); + *out = static_cast(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( - 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( - 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& in, size_t* out_len) { if (!out_len) return nullptr; *out_len = in.size(); if (in.empty()) return nullptr; - O* out = static_cast(wpi::CheckedMalloc(sizeof(O) * in.size())); + O* out = static_cast(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( - 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( - 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(wpi::CheckedMalloc(sizeof(NT_Value))); + values[i] = static_cast(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(wpi::CheckedMalloc(size * sizeof(char))); + char* retVal = static_cast(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(wpi::CheckedMalloc(size * sizeof(int))); + int* retVal = static_cast(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(wpi::CheckedMalloc(size * sizeof(double))); + static_cast(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(wpi::CheckedMalloc(size * sizeof(NT_String))); + static_cast(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(wpi::CheckedMalloc(value->data.v_string.len + 1)); + static_cast(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(wpi::CheckedMalloc(value->data.v_string.len + 1)); + static_cast(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( - 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( - 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( - 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(wpi::CheckedMalloc(len + 1)); + arr[i].str = static_cast(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(wpi::CheckedMalloc(vArr.size() * sizeof(NT_Bool))); + static_cast(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(wpi::CheckedMalloc(vArr.size() * sizeof(double))); + static_cast(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( - 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]); } diff --git a/ntcore/src/main/native/cpp/ntcore_test.cpp b/ntcore/src/main/native/cpp/ntcore_test.cpp index f74172ee97..a1919ce444 100644 --- a/ntcore/src/main/native/cpp/ntcore_test.cpp +++ b/ntcore/src/main/native/cpp/ntcore_test.cpp @@ -7,14 +7,14 @@ #include "ntcore_test.h" -#include +#include #include "Value_internal.h" extern "C" { struct NT_String* NT_GetStringForTesting(const char* string, int* struct_size) { struct NT_String* str = - static_cast(wpi::CheckedCalloc(1, sizeof(NT_String))); + static_cast(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(wpi::CheckedCalloc(1, sizeof(NT_EntryInfo))); + static_cast(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( - 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(wpi::CheckedCalloc(1, sizeof(NT_Value))); + static_cast(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(wpi::CheckedCalloc(1, sizeof(NT_Value))); + static_cast(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(wpi::CheckedCalloc(1, sizeof(NT_Value))); + static_cast(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(wpi::CheckedCalloc(1, sizeof(NT_Value))); + static_cast(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(wpi::CheckedCalloc(1, sizeof(NT_Value))); + static_cast(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(wpi::CheckedCalloc(1, sizeof(NT_Value))); + static_cast(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(wpi::CheckedCalloc(1, sizeof(NT_Value))); + static_cast(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(wpi::CheckedMalloc(len + 1)); + static_cast(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( - wpi::CheckedCalloc(1, sizeof(NT_RpcParamDef))); + struct NT_RpcParamDef* def = + static_cast(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( - 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( - 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( - 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(¶ms[i].name, &def->params[i].name); CopyNtValue(¶ms[i].def_value, &def->params[i].def_value); } def->num_results = num_results; def->results = static_cast( - 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(wpi::CheckedCalloc(1, sizeof(NT_RpcAnswer))); + static_cast(wpi::safe_calloc(1, sizeof(NT_RpcAnswer))); info->entry = rpc_id; info->call = call_uid; nt::ConvertToC(wpi::StringRef(name), &info->name); diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 2c65222522..a8ae9cd2e7 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -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$ diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp index bf51c360c9..abe3744b87 100644 --- a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp @@ -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; diff --git a/wpiutil/src/main/native/cpp/llvm/Error.cpp b/wpiutil/src/main/native/cpp/llvm/Error.cpp new file mode 100644 index 0000000000..5b87ae6b16 --- /dev/null +++ b/wpiutil/src/main/native/cpp/llvm/Error.cpp @@ -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 + +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(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 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(ErrorErrorCode::MultipleErrors), + *ErrorErrorCat); +} + +std::error_code inconvertibleErrorCode() { + return std::error_code(static_cast(ErrorErrorCode::InconvertibleError), + *ErrorErrorCat); +} + +std::error_code FileError::convertToErrorCode() const { + return std::error_code(static_cast(ErrorErrorCode::FileError), + *ErrorErrorCat); +} + +Error errorCodeToError(std::error_code EC) { + if (!EC) + return Error::success(); + return Error(wpi::make_unique(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(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 diff --git a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp index 088e80537a..497d5025f6 100644 --- a/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ErrorHandling.cpp @@ -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 +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#if defined(_MSC_VER) +#include +#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 Lock(ErrorHandlerMutex); + assert(!ErrorHandler && "Error handler already registered!\n"); + ErrorHandler = handler; + ErrorHandlerUserData = user_data; +} + +void wpi::remove_fatal_error_handler() { + std::lock_guard 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 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 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 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 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 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 diff --git a/wpiutil/src/main/native/cpp/llvm/Hashing.cpp b/wpiutil/src/main/native/cpp/llvm/Hashing.cpp index 51c033ed34..e916751518 100644 --- a/wpiutil/src/main/native/cpp/llvm/Hashing.cpp +++ b/wpiutil/src/main/native/cpp/llvm/Hashing.cpp @@ -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; } diff --git a/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp b/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp new file mode 100644 index 0000000000..0a2465f5d6 --- /dev/null +++ b/wpiutil/src/main/native/cpp/llvm/ManagedStatic.cpp @@ -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 +#include +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 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 Lock(*getManagedStaticMutex()); + + while (StaticList) + StaticList->destroy(); +} diff --git a/wpiutil/src/main/native/cpp/llvm/Path.cpp b/wpiutil/src/main/native/cpp/llvm/Path.cpp index 12736b99be..49f92d32d5 100644 --- a/wpiutil/src/main/native/cpp/llvm/Path.cpp +++ b/wpiutil/src/main/native/cpp/llvm/Path.cpp @@ -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 #include @@ -22,10 +27,8 @@ #include #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 &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 ¤t_directory, - SmallVectorImpl &path, - bool use_current_directory) { +void make_absolute(const Twine ¤t_directory, + SmallVectorImpl &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 ¤t_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 ¤t_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 ¤t_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 ¤t_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 ¤t_directory, - SmallVectorImpl &path) { - return make_absolute(current_directory, path, true); } std::error_code make_absolute(SmallVectorImpl &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 getPermissions(const Twine &Path) { @@ -829,20 +831,3 @@ ErrorOr getPermissions(const Twine &Path) { #else #include "Unix/Path.inc" #endif - -namespace wpi { -namespace sys { -namespace path { - -bool user_cache_directory(SmallVectorImpl &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 diff --git a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp index f91b6ebe8a..1dab1fcf0e 100644 --- a/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp +++ b/wpiutil/src/main/native/cpp/llvm/SmallPtrSet.cpp @@ -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 #include #include @@ -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); diff --git a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp index faa5ba7713..974fec9c5a 100644 --- a/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp +++ b/wpiutil/src/main/native/cpp/llvm/SmallVector.cpp @@ -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; } diff --git a/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp b/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp index 41c3305a8a..e4bfe8aa99 100644 --- a/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp +++ b/wpiutil/src/main/native/cpp/llvm/StringExtras.cpp @@ -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 << "&"; + else if (C == '<') + Out << "<"; + else if (C == '>') + Out << ">"; + else if (C == '\"') + Out << """; + else if (C == '\'') + Out << "'"; + else + Out << C; + } +} + void wpi::printLowerCase(StringRef String, raw_ostream &Out) { for (const char C : String) Out << toLower(C); diff --git a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp index ea91dbb98c..5c625c7aab 100644 --- a/wpiutil/src/main/native/cpp/llvm/StringMap.cpp +++ b/wpiutil/src/main/native/cpp/llvm/StringMap.cpp @@ -15,7 +15,6 @@ #include "wpi/StringExtras.h" #include "wpi/Compiler.h" #include "wpi/MathExtras.h" -#include "wpi/memory.h" #include using namespace wpi; @@ -59,7 +58,7 @@ void StringMapImpl::init(unsigned InitSize) { NumTombstones = 0; TheTable = static_cast( - 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( - 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; diff --git a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc index a1ea5a1cbb..b7868135c9 100644 --- a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc +++ b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc @@ -16,25 +16,45 @@ //=== is guaranteed to work on *all* UNIX variants. //===----------------------------------------------------------------------===// +#include "wpi/Errno.h" #include #include #include #include #include #include +#include +#include #include -#define NAMLEN(dirent) strlen((dirent)->d_name) +#include #include #include #include +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 &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(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(Mapping); +} + +const char *mapped_file_region::const_data() const { + assert(Mapping && "Mapping failed but used anyway!"); + return reinterpret_cast(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(it.IterationHandle)); - if (cur_dir == nullptr && errno != 0) { + dirent *CurDir = ::readdir(reinterpret_cast(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 *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 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 *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 openNativeFileForRead(const Twine &Name, OpenFlags Flags, + SmallVectorImpl *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 &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 &Result) { @@ -332,29 +493,6 @@ static bool getDarwinConfDir(bool TempDir, SmallVectorImpl &Result) { return false; } -static bool getUserCacheDir(SmallVectorImpl &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. diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc index 3a170ebc4e..7fcba15ecd 100644 --- a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc +++ b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc @@ -17,6 +17,7 @@ //===----------------------------------------------------------------------===// #include "wpi/STLExtras.h" +#include "wpi/ConvertUTF.h" #include "wpi/WindowsError.h" #include #include @@ -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 &result) { SmallVector cur_path; DWORD len = MAX_PATH; @@ -147,6 +174,51 @@ std::error_code current_path(SmallVectorImpl &result) { return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result); } +static std::error_code realPathFromHandle(HANDLE H, + SmallVectorImpl &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 &RealPath) { + RealPath.clear(); + SmallVector 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 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(_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(Mapping); +} + +const char *mapped_file_region::const_data() const { + assert(Mapping && "Mapping failed but used anyway!"); + return reinterpret_cast(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 path_utf16; +std::error_code detail::directory_iterator_construct(detail::DirIterState &IT, + StringRef Path, + bool FollowSymlinks) { + SmallVector 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 directory_entry::status() const { return Status; } -std::error_code openFileForRead(const Twine &Name, int &ResultFD, - SmallVectorImpl *RealPath) { - ResultFD = -1; - SmallVector PathUTF16; +static std::error_code nativeFileToFd(Expected 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 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 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 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 Result = openNativeFile(Name, Disp, Access, Flags); + if (!Result) + return errorToErrorCode(Result.takeError()); - ResultFD = -1; - SmallVector 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 &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 *RealPath) { + Expected NativeFile = openNativeFileForRead(Name, Flags, RealPath); + return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None); +} + +Expected openNativeFileForRead(const Twine &Name, OpenFlags Flags, + SmallVectorImpl *RealPath) { + Expected 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 &Result) { - return getKnownFolderPath(FOLDERID_LocalAppData, Result); -} - bool home_directory(SmallVectorImpl &result) { return getKnownFolderPath(FOLDERID_Profile, result); } diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h index 2f78a4e343..d830e330ec 100644 --- a/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h +++ b/wpiutil/src/main/native/cpp/llvm/Windows/WindowsSupport.h @@ -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 #include #include +#define WIN32_NO_STATUS #include +#undef WIN32_NO_STATUS +#include +#include + +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 ScopedRegHandle; typedef ScopedHandle ScopedFindHandle; typedef ScopedHandle ScopedJobHandle; -namespace wpi { template class SmallVectorImpl; @@ -192,21 +207,39 @@ c_str(SmallVectorImpl &str) { } namespace sys { -namespace path { -std::error_code widenPath(const Twine &Path8, - SmallVectorImpl &Path16); -} // end namespace path -namespace windows { -std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl &utf16); -/// Convert to UTF16 from the current code page used in the system -std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl &utf16); -std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, - SmallVectorImpl &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 &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. diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp index 04bae65306..a874b3a5c9 100644 --- a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp +++ b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp @@ -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(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 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() {} diff --git a/wpiutil/src/main/native/cpp/memory.cpp b/wpiutil/src/main/native/cpp/memory.cpp deleted file mode 100644 index 54e55c95b9..0000000000 --- a/wpiutil/src/main/native/cpp/memory.cpp +++ /dev/null @@ -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 - -#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 diff --git a/wpiutil/src/main/native/include/wpi/AlignOf.h b/wpiutil/src/main/native/include/wpi/AlignOf.h index 4ce34cd02d..adddd13b24 100644 --- a/wpiutil/src/main/native/include/wpi/AlignOf.h +++ b/wpiutil/src/main/native/include/wpi/AlignOf.h @@ -34,7 +34,7 @@ namespace wpi { template struct AlignedCharArray { - LLVM_ALIGNAS(Alignment) char buffer[Size]; + alignas(Alignment) char buffer[Size]; }; #else // _MSC_VER diff --git a/wpiutil/src/main/native/include/wpi/ArrayRef.h b/wpiutil/src/main/native/include/wpi/ArrayRef.h index 08f413db23..5b50563be5 100644 --- a/wpiutil/src/main/native/include/wpi/ArrayRef.h +++ b/wpiutil/src/main/native/include/wpi/ArrayRef.h @@ -26,6 +26,7 @@ #include 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. diff --git a/wpiutil/src/main/native/include/wpi/Chrono.h b/wpiutil/src/main/native/include/wpi/Chrono.h new file mode 100644 index 0000000000..4593e857bc --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/Chrono.h @@ -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 +#include + +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 +using TimePoint = std::chrono::time_point; + +/// 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(TP)); +} + +/// Convert a std::time_t to a TimePoint +LLVM_ATTRIBUTE_ALWAYS_INLINE TimePoint +toTimePoint(std::time_t T) { + using namespace std::chrono; + return time_point_cast(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(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 diff --git a/wpiutil/src/main/native/include/wpi/Compiler.h b/wpiutil/src/main/native/include/wpi/Compiler.h index 6e13ef4d1f..4a1513ec02 100644 --- a/wpiutil/src/main/native/include/wpi/Compiler.h +++ b/wpiutil/src/main/native/include/wpi/Compiler.h @@ -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 diff --git a/wpiutil/src/main/native/include/wpi/ConvertUTF.h b/wpiutil/src/main/native/include/wpi/ConvertUTF.h index c09e71a001..a9bdb606b4 100644 --- a/wpiutil/src/main/native/include/wpi/ConvertUTF.h +++ b/wpiutil/src/main/native/include/wpi/ConvertUTF.h @@ -95,6 +95,7 @@ #include #include +#include // 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 SrcUTF16, bool convertUTF8ToUTF16String(StringRef SrcUTF8, SmallVectorImpl &DstUTF16); +#if defined(_WIN32) +namespace sys { +namespace windows { +std::error_code UTF8ToUTF16(StringRef utf8, SmallVectorImpl &utf16); +/// Convert to UTF16 from the current code page used in the system +std::error_code CurCPToUTF16(StringRef utf8, SmallVectorImpl &utf16); +std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, + SmallVectorImpl &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 &utf8); +} // namespace windows +} // namespace sys +#endif + } /* end namespace wpi */ #endif diff --git a/wpiutil/src/main/native/include/wpi/DenseMap.h b/wpiutil/src/main/native/include/wpi/DenseMap.h index 2853217d38..5efc8cbf7f 100644 --- a/wpiutil/src/main/native/include/wpi/DenseMap.h +++ b/wpiutil/src/main/native/include/wpi/DenseMap.h @@ -46,9 +46,10 @@ struct DenseMapPair : public std::pair { } // end namespace detail -template < - typename KeyT, typename ValueT, typename KeyInfoT = DenseMapInfo, - typename Bucket = detail::DenseMapPair, bool IsConst = false> +template , + typename Bucket = wpi::detail::DenseMapPair, + bool IsConst = false> class DenseMapIterator; template ::value && isPodLike::value) - memcpy(getBuckets(), other.getBuckets(), + memcpy(reinterpret_cast(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 +bool operator==( + const DenseMapBase &LHS, + const DenseMapBase &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 +bool operator!=( + const DenseMapBase &LHS, + const DenseMapBase &RHS) { + return !(LHS == RHS); +} + template , - typename BucketT = detail::DenseMapPair> + typename BucketT = wpi::detail::DenseMapPair> class DenseMap : public DenseMapBase, KeyT, ValueT, KeyInfoT, BucketT> { friend class DenseMapBase; @@ -664,6 +699,11 @@ public: this->insert(I, E); } + DenseMap(std::initializer_list Vals) { + init(Vals.size()); + this->insert(Vals.begin(), Vals.end()); + } + ~DenseMap() { this->destroyAll(); operator delete(Buckets); @@ -786,7 +826,7 @@ private: template , - typename BucketT = detail::DenseMapPair> + typename BucketT = wpi::detail::DenseMapPair> class SmallDenseMap : public DenseMapBase< SmallDenseMap, KeyT, diff --git a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h index e4bf986e4c..6bc713a3a9 100644 --- a/wpiutil/src/main/native/include/wpi/DenseMapInfo.h +++ b/wpiutil/src/main/native/include/wpi/DenseMapInfo.h @@ -262,6 +262,13 @@ template struct DenseMapInfo> { } }; +template <> struct DenseMapInfo { + 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 diff --git a/wpiutil/src/main/native/include/wpi/Endian.h b/wpiutil/src/main/native/include/wpi/Endian.h new file mode 100644 index 0000000000..365209c481 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/Endian.h @@ -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 +#endif + +#include +#include +#include +#include +#include + +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 +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 +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 +inline value_type byte_swap(value_type value) { + return byte_swap(value, endian); +} + +/// Read a value of a particular endianness from memory. +template +inline value_type read(const void *memory, endianness endian) { + value_type ret; + + memcpy(&ret, + LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment::value)), + sizeof(value_type)); + return byte_swap(ret, endian); +} + +template +inline value_type read(const void *memory) { + return read(memory, endian); +} + +/// Read a value of a particular endianness from a buffer, and increment the +/// buffer past that value. +template +inline value_type readNext(const CharT *&memory, endianness endian) { + value_type ret = read(memory, endian); + memory += sizeof(value_type); + return ret; +} + +template +inline value_type readNext(const CharT *&memory) { + return readNext(memory, endian); +} + +/// Write a value to memory with a particular endianness. +template +inline void write(void *memory, value_type value, endianness endian) { + value = byte_swap(value, endian); + memcpy(LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment::value)), + &value, sizeof(value_type)); +} + +template +inline void write(void *memory, value_type value) { + write(memory, value, endian); +} + +template +using make_unsigned_t = typename std::make_unsigned::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 +inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) { + assert(startBit < 8); + if (startBit == 0) + return read(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)), + sizeof(value_type) * 2); + val[0] = byte_swap(val[0]); + val[1] = byte_swap(val[1]); + + // Shift bits from the lower value into place. + make_unsigned_t lowerVal = val[0] >> startBit; + // Mask off upper bits after right shift in case of signed type. + make_unsigned_t numBitsFirstVal = + (sizeof(value_type) * 8) - startBit; + lowerVal &= ((make_unsigned_t)1 << numBitsFirstVal) - 1; + + // Get the bits from the upper value. + make_unsigned_t upperVal = + val[1] & (((make_unsigned_t)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 +inline void writeAtBitAlignment(void *memory, value_type value, + uint64_t startBit) { + assert(startBit < 8); + if (startBit == 0) + write(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)), + sizeof(value_type) * 2); + val[0] = byte_swap(val[0]); + val[1] = byte_swap(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)1 << startBit) - 1; + make_unsigned_t numBitsFirstVal = + (sizeof(value_type) * 8) - startBit; + make_unsigned_t 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)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)1 << startBit) - 1); + // Next shift the bits that go into the upper value into position. + make_unsigned_t upperVal = value >> numBitsFirstVal; + // Mask off upper bits after right shift in case of signed type. + upperVal &= ((make_unsigned_t)1 << startBit) - 1; + val[1] |= upperVal; + + // Finally, rewrite values. + val[0] = byte_swap(val[0]); + val[1] = byte_swap(val[1]); + memcpy(LLVM_ASSUME_ALIGNED( + memory, (detail::PickAlignment::value)), + &val[0], sizeof(value_type) * 2); + } +} + +} // end namespace endian + +namespace detail { + +template +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( + (const void*)Value.buffer); + } + + void operator=(value_type newValue) { + endian::write( + (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::value, + sizeof(value_type)> Value; + +public: + struct ref { + explicit ref(void *Ptr) : Ptr(Ptr) {} + + operator value_type() const { + return endian::read(Ptr); + } + + void operator=(value_type NewValue) { + endian::write(Ptr, NewValue); + } + + private: + void *Ptr; + }; +}; + +} // end namespace detail + +using ulittle16_t = + detail::packed_endian_specific_integral; +using ulittle32_t = + detail::packed_endian_specific_integral; +using ulittle64_t = + detail::packed_endian_specific_integral; + +using little16_t = + detail::packed_endian_specific_integral; +using little32_t = + detail::packed_endian_specific_integral; +using little64_t = + detail::packed_endian_specific_integral; + +using aligned_ulittle16_t = + detail::packed_endian_specific_integral; +using aligned_ulittle32_t = + detail::packed_endian_specific_integral; +using aligned_ulittle64_t = + detail::packed_endian_specific_integral; + +using aligned_little16_t = + detail::packed_endian_specific_integral; +using aligned_little32_t = + detail::packed_endian_specific_integral; +using aligned_little64_t = + detail::packed_endian_specific_integral; + +using ubig16_t = + detail::packed_endian_specific_integral; +using ubig32_t = + detail::packed_endian_specific_integral; +using ubig64_t = + detail::packed_endian_specific_integral; + +using big16_t = + detail::packed_endian_specific_integral; +using big32_t = + detail::packed_endian_specific_integral; +using big64_t = + detail::packed_endian_specific_integral; + +using aligned_ubig16_t = + detail::packed_endian_specific_integral; +using aligned_ubig32_t = + detail::packed_endian_specific_integral; +using aligned_ubig64_t = + detail::packed_endian_specific_integral; + +using aligned_big16_t = + detail::packed_endian_specific_integral; +using aligned_big32_t = + detail::packed_endian_specific_integral; +using aligned_big64_t = + detail::packed_endian_specific_integral; + +using unaligned_uint16_t = + detail::packed_endian_specific_integral; +using unaligned_uint32_t = + detail::packed_endian_specific_integral; +using unaligned_uint64_t = + detail::packed_endian_specific_integral; + +using unaligned_int16_t = + detail::packed_endian_specific_integral; +using unaligned_int32_t = + detail::packed_endian_specific_integral; +using unaligned_int64_t = + detail::packed_endian_specific_integral; + +namespace endian { + +template inline T read(const void *P, endianness E) { + return read(P, E); +} + +template inline T read(const void *P) { + return *(const detail::packed_endian_specific_integral *)P; +} + +inline uint16_t read16(const void *P, endianness E) { + return read(P, E); +} +inline uint32_t read32(const void *P, endianness E) { + return read(P, E); +} +inline uint64_t read64(const void *P, endianness E) { + return read(P, E); +} + +template inline uint16_t read16(const void *P) { + return read(P); +} +template inline uint32_t read32(const void *P) { + return read(P); +} +template inline uint64_t read64(const void *P) { + return read(P); +} + +inline uint16_t read16le(const void *P) { return read16(P); } +inline uint32_t read32le(const void *P) { return read32(P); } +inline uint64_t read64le(const void *P) { return read64(P); } +inline uint16_t read16be(const void *P) { return read16(P); } +inline uint32_t read32be(const void *P) { return read32(P); } +inline uint64_t read64be(const void *P) { return read64(P); } + +template inline void write(void *P, T V, endianness E) { + write(P, V, E); +} + +template inline void write(void *P, T V) { + *(detail::packed_endian_specific_integral *)P = V; +} + +inline void write16(void *P, uint16_t V, endianness E) { + write(P, V, E); +} +inline void write32(void *P, uint32_t V, endianness E) { + write(P, V, E); +} +inline void write64(void *P, uint64_t V, endianness E) { + write(P, V, E); +} + +template inline void write16(void *P, uint16_t V) { + write(P, V); +} +template inline void write32(void *P, uint32_t V) { + write(P, V); +} +template inline void write64(void *P, uint64_t V) { + write(P, V); +} + +inline void write16le(void *P, uint16_t V) { write16(P, V); } +inline void write32le(void *P, uint32_t V) { write32(P, V); } +inline void write64le(void *P, uint64_t V) { write64(P, V); } +inline void write16be(void *P, uint16_t V) { write16(P, V); } +inline void write32be(void *P, uint32_t V) { write32(P, V); } +inline void write64be(void *P, uint64_t V) { write64(P, V); } + +} // end namespace endian + +} // end namespace support +} // end namespace wpi + +#endif // WPIUTIL_WPI_ENDIAN_H diff --git a/wpiutil/src/main/native/include/wpi/Errc.h b/wpiutil/src/main/native/include/wpi/Errc.h new file mode 100644 index 0000000000..ebce58ada7 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/Errc.h @@ -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 + +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(E), std::generic_category()); +} +} + +namespace std { +template <> struct is_error_code_enum : std::true_type {}; +} +#endif diff --git a/wpiutil/src/main/native/include/wpi/Errno.h b/wpiutil/src/main/native/include/wpi/Errno.h new file mode 100644 index 0000000000..042d43212b --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/Errno.h @@ -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 +#include +#include + +namespace wpi { +namespace sys { + +template +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 diff --git a/wpiutil/src/main/native/include/wpi/Error.h b/wpiutil/src/main/native/include/wpi/Error.h new file mode 100644 index 0000000000..3ce87206cc --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/Error.h @@ -0,0 +1,1197 @@ +//===- llvm/Support/Error.h - Recoverable 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 report recoverable errors. +// +//===----------------------------------------------------------------------===// + +#ifndef WPIUTIL_WPI_ERROR_H +#define WPIUTIL_WPI_ERROR_H + +#include "wpi/STLExtras.h" +#include "wpi/SmallVector.h" +#include "wpi/StringExtras.h" +#include "wpi/Twine.h" +#include "wpi/AlignOf.h" +#include "wpi/Compiler.h" +#include "wpi/ErrorHandling.h" +#include "wpi/ErrorOr.h" +#include "wpi/Format.h" +#include "wpi/raw_ostream.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wpi { + +class ErrorSuccess; + +/// Base class for error info classes. Do not extend this directly: Extend +/// the ErrorInfo template subclass instead. +class ErrorInfoBase { +public: + virtual ~ErrorInfoBase() = default; + + /// Print an error message to an output stream. + virtual void log(raw_ostream &OS) const = 0; + + /// Return the error message as a string. + virtual std::string message() const { + std::string Msg; + raw_string_ostream OS(Msg); + log(OS); + return OS.str(); + } + + /// Convert this error to a std::error_code. + /// + /// This is a temporary crutch to enable interaction with code still + /// using std::error_code. It will be removed in the future. + virtual std::error_code convertToErrorCode() const = 0; + + // Returns the class ID for this type. + static const void *classID() { return &ID; } + + // Returns the class ID for the dynamic type of this ErrorInfoBase instance. + virtual const void *dynamicClassID() const = 0; + + // Check whether this instance is a subclass of the class identified by + // ClassID. + virtual bool isA(const void *const ClassID) const { + return ClassID == classID(); + } + + // Check whether this instance is a subclass of ErrorInfoT. + template bool isA() const { + return isA(ErrorInfoT::classID()); + } + +private: + virtual void anchor(); + + static char ID; +}; + +/// Lightweight error class with error context and mandatory checking. +/// +/// Instances of this class wrap a ErrorInfoBase pointer. Failure states +/// are represented by setting the pointer to a ErrorInfoBase subclass +/// instance containing information describing the failure. Success is +/// represented by a null pointer value. +/// +/// Instances of Error also contains a 'Checked' flag, which must be set +/// before the destructor is called, otherwise the destructor will trigger a +/// runtime error. This enforces at runtime the requirement that all Error +/// instances be checked or returned to the caller. +/// +/// There are two ways to set the checked flag, depending on what state the +/// Error instance is in. For Error instances indicating success, it +/// is sufficient to invoke the boolean conversion operator. E.g.: +/// +/// @code{.cpp} +/// Error foo(<...>); +/// +/// if (auto E = foo(<...>)) +/// return E; // <- Return E if it is in the error state. +/// // We have verified that E was in the success state. It can now be safely +/// // destroyed. +/// @endcode +/// +/// A success value *can not* be dropped. For example, just calling 'foo(<...>)' +/// without testing the return value will raise a runtime error, even if foo +/// returns success. +/// +/// For Error instances representing failure, you must use either the +/// handleErrors or handleAllErrors function with a typed handler. E.g.: +/// +/// @code{.cpp} +/// class MyErrorInfo : public ErrorInfo { +/// // Custom error info. +/// }; +/// +/// Error foo(<...>) { return make_error(...); } +/// +/// auto E = foo(<...>); // <- foo returns failure with MyErrorInfo. +/// auto NewE = +/// handleErrors(E, +/// [](const MyErrorInfo &M) { +/// // Deal with the error. +/// }, +/// [](std::unique_ptr M) -> Error { +/// if (canHandle(*M)) { +/// // handle error. +/// return Error::success(); +/// } +/// // Couldn't handle this error instance. Pass it up the stack. +/// return Error(std::move(M)); +/// ); +/// // Note - we must check or return NewE in case any of the handlers +/// // returned a new error. +/// @endcode +/// +/// The handleAllErrors function is identical to handleErrors, except +/// that it has a void return type, and requires all errors to be handled and +/// no new errors be returned. It prevents errors (assuming they can all be +/// handled) from having to be bubbled all the way to the top-level. +/// +/// *All* Error instances must be checked before destruction, even if +/// they're moved-assigned or constructed from Success values that have already +/// been checked. This enforces checking through all levels of the call stack. +class LLVM_NODISCARD Error { + // Both ErrorList and FileError need to be able to yank ErrorInfoBase + // pointers out of this class to add to the error list. + friend class ErrorList; + friend class FileError; + + // handleErrors needs to be able to set the Checked flag. + template + friend Error handleErrors(Error E, HandlerTs &&... Handlers); + + // Expected needs to be able to steal the payload when constructed from an + // error. + template friend class Expected; + +protected: + /// Create a success value. Prefer using 'Error::success()' for readability + Error() { + setPtr(nullptr); + setChecked(false); + } + +public: + /// Create a success value. + static ErrorSuccess success(); + + // Errors are not copy-constructable. + Error(const Error &Other) = delete; + + /// Move-construct an error value. The newly constructed error is considered + /// unchecked, even if the source error had been checked. The original error + /// becomes a checked Success value, regardless of its original state. + Error(Error &&Other) { + setChecked(true); + *this = std::move(Other); + } + + /// Create an error value. Prefer using the 'make_error' function, but + /// this constructor can be useful when "re-throwing" errors from handlers. + Error(std::unique_ptr Payload) { + setPtr(Payload.release()); + setChecked(false); + } + + // Errors are not copy-assignable. + Error &operator=(const Error &Other) = delete; + + /// Move-assign an error value. The current error must represent success, you + /// you cannot overwrite an unhandled error. The current error is then + /// considered unchecked. The source error becomes a checked success value, + /// regardless of its original state. + Error &operator=(Error &&Other) { + // Don't allow overwriting of unchecked values. + assertIsChecked(); + setPtr(Other.getPtr()); + + // This Error is unchecked, even if the source error was checked. + setChecked(false); + + // Null out Other's payload and set its checked bit. + Other.setPtr(nullptr); + Other.setChecked(true); + + return *this; + } + + /// Destroy a Error. Fails with a call to abort() if the error is + /// unchecked. + ~Error() { + assertIsChecked(); + delete getPtr(); + } + + /// Bool conversion. Returns true if this Error is in a failure state, + /// and false if it is in an accept state. If the error is in a Success state + /// it will be considered checked. + explicit operator bool() { + setChecked(getPtr() == nullptr); + return getPtr() != nullptr; + } + + /// Check whether one error is a subclass of another. + template bool isA() const { + return getPtr() && getPtr()->isA(ErrT::classID()); + } + + /// Returns the dynamic class id of this error, or null if this is a success + /// value. + const void* dynamicClassID() const { + if (!getPtr()) + return nullptr; + return getPtr()->dynamicClassID(); + } + +private: + void assertIsChecked() { + } + + ErrorInfoBase *getPtr() const { + return reinterpret_cast( + reinterpret_cast(Payload) & + ~static_cast(0x1)); + } + + void setPtr(ErrorInfoBase *EI) { + Payload = EI; + } + + bool getChecked() const { + return true; + } + + void setChecked(bool V) { + Payload = reinterpret_cast( + (reinterpret_cast(Payload) & + ~static_cast(0x1)) | + (V ? 0 : 1)); + } + + std::unique_ptr takePayload() { + std::unique_ptr Tmp(getPtr()); + setPtr(nullptr); + setChecked(true); + return Tmp; + } + + friend raw_ostream &operator<<(raw_ostream &OS, const Error &E) { + if (auto P = E.getPtr()) + P->log(OS); + else + OS << "success"; + return OS; + } + + ErrorInfoBase *Payload = nullptr; +}; + +/// Subclass of Error for the sole purpose of identifying the success path in +/// the type system. This allows to catch invalid conversion to Expected at +/// compile time. +class ErrorSuccess final : public Error {}; + +inline ErrorSuccess Error::success() { return ErrorSuccess(); } + +/// Make a Error instance representing failure using the given error info +/// type. +template Error make_error(ArgTs &&... Args) { + return Error(wpi::make_unique(std::forward(Args)...)); +} + +/// Base class for user error types. Users should declare their error types +/// like: +/// +/// class MyError : public ErrorInfo { +/// .... +/// }; +/// +/// This class provides an implementation of the ErrorInfoBase::kind +/// method, which is used by the Error RTTI system. +template +class ErrorInfo : public ParentErrT { +public: + using ParentErrT::ParentErrT; // inherit constructors + + static const void *classID() { return &ThisErrT::ID; } + + const void *dynamicClassID() const override { return &ThisErrT::ID; } + + bool isA(const void *const ClassID) const override { + return ClassID == classID() || ParentErrT::isA(ClassID); + } +}; + +/// Special ErrorInfo subclass representing a list of ErrorInfos. +/// Instances of this class are constructed by joinError. +class ErrorList final : public ErrorInfo { + // handleErrors needs to be able to iterate the payload list of an + // ErrorList. + template + friend Error handleErrors(Error E, HandlerTs &&... Handlers); + + // joinErrors is implemented in terms of join. + friend Error joinErrors(Error, Error); + +public: + void log(raw_ostream &OS) const override { + OS << "Multiple errors:\n"; + for (auto &ErrPayload : Payloads) { + ErrPayload->log(OS); + OS << "\n"; + } + } + + std::error_code convertToErrorCode() const override; + + // Used by ErrorInfo::classID. + static char ID; + +private: + ErrorList(std::unique_ptr Payload1, + std::unique_ptr Payload2) { + assert(!Payload1->isA() && !Payload2->isA() && + "ErrorList constructor payloads should be singleton errors"); + Payloads.push_back(std::move(Payload1)); + Payloads.push_back(std::move(Payload2)); + } + + static Error join(Error E1, Error E2) { + if (!E1) + return E2; + if (!E2) + return E1; + if (E1.isA()) { + auto &E1List = static_cast(*E1.getPtr()); + if (E2.isA()) { + auto E2Payload = E2.takePayload(); + auto &E2List = static_cast(*E2Payload); + for (auto &Payload : E2List.Payloads) + E1List.Payloads.push_back(std::move(Payload)); + } else + E1List.Payloads.push_back(E2.takePayload()); + + return E1; + } + if (E2.isA()) { + auto &E2List = static_cast(*E2.getPtr()); + E2List.Payloads.insert(E2List.Payloads.begin(), E1.takePayload()); + return E2; + } + return Error(std::unique_ptr( + new ErrorList(E1.takePayload(), E2.takePayload()))); + } + + std::vector> Payloads; +}; + +/// Concatenate errors. The resulting Error is unchecked, and contains the +/// ErrorInfo(s), if any, contained in E1, followed by the +/// ErrorInfo(s), if any, contained in E2. +inline Error joinErrors(Error E1, Error E2) { + return ErrorList::join(std::move(E1), std::move(E2)); +} + +/// Tagged union holding either a T or a Error. +/// +/// This class parallels ErrorOr, but replaces error_code with Error. Since +/// Error cannot be copied, this class replaces getError() with +/// takeError(). It also adds an bool errorIsA() method for testing the +/// error class type. +template class LLVM_NODISCARD Expected { + template friend class ExpectedAsOutParameter; + template friend class Expected; + + static const bool isRef = std::is_reference::value; + + using wrap = std::reference_wrapper::type>; + + using error_type = std::unique_ptr; + +public: + using storage_type = typename std::conditional::type; + using value_type = T; + +private: + using reference = typename std::remove_reference::type &; + using const_reference = const typename std::remove_reference::type &; + using pointer = typename std::remove_reference::type *; + using const_pointer = const typename std::remove_reference::type *; + +public: + /// Create an Expected error value from the given Error. + Expected(Error Err) + : HasError(true) + { + assert(Err && "Cannot create Expected from Error success value."); + new (getErrorStorage()) error_type(Err.takePayload()); + } + + /// Forbid to convert from Error::success() implicitly, this avoids having + /// Expected foo() { return Error::success(); } which compiles otherwise + /// but triggers the assertion above. + Expected(ErrorSuccess) = delete; + + /// Create an Expected success value from the given OtherT value, which + /// must be convertible to T. + template + Expected(OtherT &&Val, + typename std::enable_if::value>::type + * = nullptr) + : HasError(false) + { + new (getStorage()) storage_type(std::forward(Val)); + } + + /// Move construct an Expected value. + Expected(Expected &&Other) { moveConstruct(std::move(Other)); } + + /// Move construct an Expected value from an Expected, where OtherT + /// must be convertible to T. + template + Expected(Expected &&Other, + typename std::enable_if::value>::type + * = nullptr) { + moveConstruct(std::move(Other)); + } + + /// Move construct an Expected value from an Expected, where OtherT + /// isn't convertible to T. + template + explicit Expected( + Expected &&Other, + typename std::enable_if::value>::type * = + nullptr) { + moveConstruct(std::move(Other)); + } + + /// Move-assign from another Expected. + Expected &operator=(Expected &&Other) { + moveAssign(std::move(Other)); + return *this; + } + + /// Destroy an Expected. + ~Expected() { + assertIsChecked(); + if (!HasError) + getStorage()->~storage_type(); + else + getErrorStorage()->~error_type(); + } + + /// Return false if there is an error. + explicit operator bool() { + return !HasError; + } + + /// Returns a reference to the stored T value. + reference get() { + assertIsChecked(); + return *getStorage(); + } + + /// Returns a const reference to the stored T value. + const_reference get() const { + assertIsChecked(); + return const_cast *>(this)->get(); + } + + /// Check that this Expected is an error of type ErrT. + template bool errorIsA() const { + return HasError && (*getErrorStorage())->template isA(); + } + + /// Take ownership of the stored error. + /// After calling this the Expected is in an indeterminate state that can + /// only be safely destructed. No further calls (beside the destructor) should + /// be made on the Expected vaule. + Error takeError() { + return HasError ? Error(std::move(*getErrorStorage())) : Error::success(); + } + + /// Returns a pointer to the stored T value. + pointer operator->() { + assertIsChecked(); + return toPointer(getStorage()); + } + + /// Returns a const pointer to the stored T value. + const_pointer operator->() const { + assertIsChecked(); + return toPointer(getStorage()); + } + + /// Returns a reference to the stored T value. + reference operator*() { + assertIsChecked(); + return *getStorage(); + } + + /// Returns a const reference to the stored T value. + const_reference operator*() const { + assertIsChecked(); + return *getStorage(); + } + +private: + template + static bool compareThisIfSameType(const T1 &a, const T1 &b) { + return &a == &b; + } + + template + static bool compareThisIfSameType(const T1 &a, const T2 &b) { + return false; + } + + template void moveConstruct(Expected &&Other) { + HasError = Other.HasError; + + if (!HasError) + new (getStorage()) storage_type(std::move(*Other.getStorage())); + else + new (getErrorStorage()) error_type(std::move(*Other.getErrorStorage())); + } + + template void moveAssign(Expected &&Other) { + assertIsChecked(); + + if (compareThisIfSameType(*this, Other)) + return; + + this->~Expected(); + new (this) Expected(std::move(Other)); + } + + pointer toPointer(pointer Val) { return Val; } + + const_pointer toPointer(const_pointer Val) const { return Val; } + + pointer toPointer(wrap *Val) { return &Val->get(); } + + const_pointer toPointer(const wrap *Val) const { return &Val->get(); } + + storage_type *getStorage() { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast(TStorage.buffer); + } + + const storage_type *getStorage() const { + assert(!HasError && "Cannot get value when an error exists!"); + return reinterpret_cast(TStorage.buffer); + } + + error_type *getErrorStorage() { + assert(HasError && "Cannot get error when a value exists!"); + return reinterpret_cast(ErrorStorage.buffer); + } + + const error_type *getErrorStorage() const { + assert(HasError && "Cannot get error when a value exists!"); + return reinterpret_cast(ErrorStorage.buffer); + } + + // Used by ExpectedAsOutParameter to reset the checked flag. + void setUnchecked() { + } + + void assertIsChecked() { + } + + union { + AlignedCharArrayUnion TStorage; + AlignedCharArrayUnion ErrorStorage; + }; + bool HasError : 1; +}; + +/// Report a serious error, calling any installed error handler. See +/// ErrorHandling.h. +LLVM_ATTRIBUTE_NORETURN void report_fatal_error(Error Err, + bool gen_crash_diag = true); + +/// Report a fatal error if Err is a failure value. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns +/// // Error::success(). +/// Error foo(bool DoFallibleOperation); +/// +/// cantFail(foo(false)); +/// @endcode +inline void cantFail(Error Err, const char *Msg = nullptr) { + if (Err) { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + wpi_unreachable(Msg); + } +} + +/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and +/// returns the contained value. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns an int. +/// Expected foo(bool DoFallibleOperation); +/// +/// int X = cantFail(foo(false)); +/// @endcode +template +T cantFail(Expected ValOrErr, const char *Msg = nullptr) { + if (ValOrErr) + return std::move(*ValOrErr); + else { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + wpi_unreachable(Msg); + } +} + +/// Report a fatal error if ValOrErr is a failure value, otherwise unwraps and +/// returns the contained reference. +/// +/// This function can be used to wrap calls to fallible functions ONLY when it +/// is known that the Error will always be a success value. E.g. +/// +/// @code{.cpp} +/// // foo only attempts the fallible operation if DoFallibleOperation is +/// // true. If DoFallibleOperation is false then foo always returns a Bar&. +/// Expected foo(bool DoFallibleOperation); +/// +/// Bar &X = cantFail(foo(false)); +/// @endcode +template +T& cantFail(Expected ValOrErr, const char *Msg = nullptr) { + if (ValOrErr) + return *ValOrErr; + else { + if (!Msg) + Msg = "Failure value returned from cantFail wrapped call"; + wpi_unreachable(Msg); + } +} + +/// Helper for testing applicability of, and applying, handlers for +/// ErrorInfo types. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits::type::operator())> {}; + +// Specialization functions of the form 'Error (const ErrT&)'. +template class ErrorHandlerTraits { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + return H(static_cast(*E)); + } +}; + +// Specialization functions of the form 'void (const ErrT&)'. +template class ErrorHandlerTraits { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + H(static_cast(*E)); + return Error::success(); + } +}; + +/// Specialization for functions of the form 'Error (std::unique_ptr)'. +template +class ErrorHandlerTraits)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + std::unique_ptr SubE(static_cast(E.release())); + return H(std::move(SubE)); + } +}; + +/// Specialization for functions of the form 'void (std::unique_ptr)'. +template +class ErrorHandlerTraits)> { +public: + static bool appliesTo(const ErrorInfoBase &E) { + return E.template isA(); + } + + template + static Error apply(HandlerT &&H, std::unique_ptr E) { + assert(appliesTo(*E) && "Applying incorrect handler"); + std::unique_ptr SubE(static_cast(E.release())); + H(std::move(SubE)); + return Error::success(); + } +}; + +// Specialization for member functions of the form 'RetT (const ErrT&)'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +// Specialization for member functions of the form 'RetT (const ErrT&) const'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +// Specialization for member functions of the form 'RetT (const ErrT&)'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +// Specialization for member functions of the form 'RetT (const ErrT&) const'. +template +class ErrorHandlerTraits + : public ErrorHandlerTraits {}; + +/// Specialization for member functions of the form +/// 'RetT (std::unique_ptr)'. +template +class ErrorHandlerTraits)> + : public ErrorHandlerTraits)> {}; + +/// Specialization for member functions of the form +/// 'RetT (std::unique_ptr) const'. +template +class ErrorHandlerTraits) const> + : public ErrorHandlerTraits)> {}; + +inline Error handleErrorImpl(std::unique_ptr Payload) { + return Error(std::move(Payload)); +} + +template +Error handleErrorImpl(std::unique_ptr Payload, + HandlerT &&Handler, HandlerTs &&... Handlers) { + if (ErrorHandlerTraits::appliesTo(*Payload)) + return ErrorHandlerTraits::apply(std::forward(Handler), + std::move(Payload)); + return handleErrorImpl(std::move(Payload), + std::forward(Handlers)...); +} + +/// Pass the ErrorInfo(s) contained in E to their respective handlers. Any +/// unhandled errors (or Errors returned by handlers) are re-concatenated and +/// returned. +/// Because this function returns an error, its result must also be checked +/// or returned. If you intend to handle all errors use handleAllErrors +/// (which returns void, and will abort() on unhandled errors) instead. +template +Error handleErrors(Error E, HandlerTs &&... Hs) { + if (!E) + return Error::success(); + + std::unique_ptr Payload = E.takePayload(); + + if (Payload->isA()) { + ErrorList &List = static_cast(*Payload); + Error R; + for (auto &P : List.Payloads) + R = ErrorList::join( + std::move(R), + handleErrorImpl(std::move(P), std::forward(Hs)...)); + return R; + } + + return handleErrorImpl(std::move(Payload), std::forward(Hs)...); +} + +/// Behaves the same as handleErrors, except that by contract all errors +/// *must* be handled by the given handlers (i.e. there must be no remaining +/// errors after running the handlers, or wpi_unreachable is called). +template +void handleAllErrors(Error E, HandlerTs &&... Handlers) { + cantFail(handleErrors(std::move(E), std::forward(Handlers)...)); +} + +/// Check that E is a non-error, then drop it. +/// If E is an error, wpi_unreachable will be called. +inline void handleAllErrors(Error E) { + cantFail(std::move(E)); +} + +/// Handle any errors (if present) in an Expected, then try a recovery path. +/// +/// If the incoming value is a success value it is returned unmodified. If it +/// is a failure value then it the contained error is passed to handleErrors. +/// If handleErrors is able to handle the error then the RecoveryPath functor +/// is called to supply the final result. If handleErrors is not able to +/// handle all errors then the unhandled errors are returned. +/// +/// This utility enables the follow pattern: +/// +/// @code{.cpp} +/// enum FooStrategy { Aggressive, Conservative }; +/// Expected foo(FooStrategy S); +/// +/// auto ResultOrErr = +/// handleExpected( +/// foo(Aggressive), +/// []() { return foo(Conservative); }, +/// [](AggressiveStrategyError&) { +/// // Implicitly conusme this - we'll recover by using a conservative +/// // strategy. +/// }); +/// +/// @endcode +template +Expected handleExpected(Expected ValOrErr, RecoveryFtor &&RecoveryPath, + HandlerTs &&... Handlers) { + if (ValOrErr) + return ValOrErr; + + if (auto Err = handleErrors(ValOrErr.takeError(), + std::forward(Handlers)...)) + return std::move(Err); + + return RecoveryPath(); +} + +/// Log all errors (if any) in E to OS. If there are any errors, ErrorBanner +/// will be printed before the first one is logged. A newline will be printed +/// after each error. +/// +/// This function is compatible with the helpers from Support/WithColor.h. You +/// can pass any of them as the OS. Please consider using them instead of +/// including 'error: ' in the ErrorBanner. +/// +/// This is useful in the base level of your program to allow clean termination +/// (allowing clean deallocation of resources, etc.), while reporting error +/// information to the user. +void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner = {}); + +/// Write all error messages (if any) in E to a string. The newline character +/// is used to separate error messages. +inline std::string toString(Error E) { + SmallVector Errors; + handleAllErrors(std::move(E), [&Errors](const ErrorInfoBase &EI) { + Errors.push_back(EI.message()); + }); + return join(Errors.begin(), Errors.end(), "\n"); +} + +/// Consume a Error without doing anything. This method should be used +/// only where an error can be considered a reasonable and expected return +/// value. +/// +/// Uses of this method are potentially indicative of design problems: If it's +/// legitimate to do nothing while processing an "error", the error-producer +/// might be more clearly refactored to return an Optional. +inline void consumeError(Error Err) { + handleAllErrors(std::move(Err), [](const ErrorInfoBase &) {}); +} + +/// Helper for converting an Error to a bool. +/// +/// This method returns true if Err is in an error state, or false if it is +/// in a success state. Puts Err in a checked state in both cases (unlike +/// Error::operator bool(), which only does this for success states). +inline bool errorToBool(Error Err) { + bool IsError = static_cast(Err); + if (IsError) + consumeError(std::move(Err)); + return IsError; +} + +/// Helper for Errors used as out-parameters. +/// +/// This helper is for use with the Error-as-out-parameter idiom, where an error +/// is passed to a function or method by reference, rather than being returned. +/// In such cases it is helpful to set the checked bit on entry to the function +/// so that the error can be written to (unchecked Errors abort on assignment) +/// and clear the checked bit on exit so that clients cannot accidentally forget +/// to check the result. This helper performs these actions automatically using +/// RAII: +/// +/// @code{.cpp} +/// Result foo(Error &Err) { +/// ErrorAsOutParameter ErrAsOutParam(&Err); // 'Checked' flag set +/// // +/// // <- 'Checked' flag auto-cleared when ErrAsOutParam is destructed. +/// } +/// @endcode +/// +/// ErrorAsOutParameter takes an Error* rather than Error& so that it can be +/// used with optional Errors (Error pointers that are allowed to be null). If +/// ErrorAsOutParameter took an Error reference, an instance would have to be +/// created inside every condition that verified that Error was non-null. By +/// taking an Error pointer we can just create one instance at the top of the +/// function. +class ErrorAsOutParameter { +public: + ErrorAsOutParameter(Error *Err) : Err(Err) { + // Raise the checked bit if Err is success. + if (Err) + (void)!!*Err; + } + + ~ErrorAsOutParameter() { + // Clear the checked bit. + if (Err && !*Err) + *Err = Error::success(); + } + +private: + Error *Err; +}; + +/// Helper for Expecteds used as out-parameters. +/// +/// See ErrorAsOutParameter. +template +class ExpectedAsOutParameter { +public: + ExpectedAsOutParameter(Expected *ValOrErr) + : ValOrErr(ValOrErr) { + if (ValOrErr) + (void)!!*ValOrErr; + } + + ~ExpectedAsOutParameter() { + if (ValOrErr) + ValOrErr->setUnchecked(); + } + +private: + Expected *ValOrErr; +}; + +/// This class wraps a std::error_code in a Error. +/// +/// This is useful if you're writing an interface that returns a Error +/// (or Expected) and you want to call code that still returns +/// std::error_codes. +class ECError : public ErrorInfo { + friend Error errorCodeToError(std::error_code); + + virtual void anchor() override; + +public: + void setErrorCode(std::error_code EC) { this->EC = EC; } + std::error_code convertToErrorCode() const override { return EC; } + void log(raw_ostream &OS) const override { OS << EC.message(); } + + // Used by ErrorInfo::classID. + static char ID; + +protected: + ECError() = default; + ECError(std::error_code EC) : EC(EC) {} + + std::error_code EC; +}; + +/// The value returned by this function can be returned from convertToErrorCode +/// for Error values where no sensible translation to std::error_code exists. +/// It should only be used in this situation, and should never be used where a +/// sensible conversion to std::error_code is available, as attempts to convert +/// to/from this error will result in a fatal error. (i.e. it is a programmatic +///error to try to convert such a value). +std::error_code inconvertibleErrorCode(); + +/// Helper for converting an std::error_code to a Error. +Error errorCodeToError(std::error_code EC); + +/// Helper for converting an ECError to a std::error_code. +/// +/// This method requires that Err be Error() or an ECError, otherwise it +/// will trigger a call to abort(). +std::error_code errorToErrorCode(Error Err); + +/// Convert an ErrorOr to an Expected. +template Expected errorOrToExpected(ErrorOr &&EO) { + if (auto EC = EO.getError()) + return errorCodeToError(EC); + return std::move(*EO); +} + +/// Convert an Expected to an ErrorOr. +template ErrorOr expectedToErrorOr(Expected &&E) { + if (auto Err = E.takeError()) + return errorToErrorCode(std::move(Err)); + return std::move(*E); +} + +/// This class wraps a string in an Error. +/// +/// StringError is useful in cases where the client is not expected to be able +/// to consume the specific error message programmatically (for example, if the +/// error message is to be presented to the user). +/// +/// StringError can also be used when additional information is to be printed +/// along with a error_code message. Depending on the constructor called, this +/// class can either display: +/// 1. the error_code message (ECError behavior) +/// 2. a string +/// 3. the error_code message and a string +/// +/// These behaviors are useful when subtyping is required; for example, when a +/// specific library needs an explicit error type. In the example below, +/// PDBError is derived from StringError: +/// +/// @code{.cpp} +/// Expected foo() { +/// return wpi::make_error(pdb_error_code::dia_failed_loading, +/// "Additional information"); +/// } +/// @endcode +/// +class StringError : public ErrorInfo { +public: + static char ID; + + // Prints EC + S and converts to EC + StringError(std::error_code EC, const Twine &S = Twine()); + + // Prints S and converts to EC + StringError(const Twine &S, std::error_code EC); + + void log(raw_ostream &OS) const override; + std::error_code convertToErrorCode() const override; + + const std::string &getMessage() const { return Msg; } + +private: + std::string Msg; + std::error_code EC; + const bool PrintMsgOnly = false; +}; + +/// Create formatted StringError object. +template +Error createStringError(std::error_code EC, char const *Fmt, + const Ts &... Vals) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + Stream << format(Fmt, Vals...); + return make_error(Stream.str(), EC); +} + +Error createStringError(std::error_code EC, char const *Msg); + +/// This class wraps a filename and another Error. +/// +/// In some cases, an error needs to live along a 'source' name, in order to +/// show more detailed information to the user. +class FileError final : public ErrorInfo { + + friend Error createFileError(std::string, Error); + +public: + void log(raw_ostream &OS) const override { + assert(Err && !FileName.empty() && "Trying to log after takeError()."); + OS << "'" << FileName << "': "; + Err->log(OS); + } + + Error takeError() { return Error(std::move(Err)); } + + std::error_code convertToErrorCode() const override; + + // Used by ErrorInfo::classID. + static char ID; + +private: + FileError(std::string F, std::unique_ptr E) { + assert(E && "Cannot create FileError from Error success value."); + assert(!F.empty() && + "The file name provided to FileError must not be empty."); + FileName = F; + Err = std::move(E); + } + + static Error build(std::string F, Error E) { + return Error(std::unique_ptr(new FileError(F, E.takePayload()))); + } + + std::string FileName; + std::unique_ptr Err; +}; + +/// Concatenate a source file path and/or name with an Error. The resulting +/// Error is unchecked. +inline Error createFileError(std::string F, Error E) { + return FileError::build(F, std::move(E)); +} + +Error createFileError(std::string F, ErrorSuccess) = delete; + +/// Helper for check-and-exit error handling. +/// +/// For tool use only. NOT FOR USE IN LIBRARY CODE. +/// +class ExitOnError { +public: + /// Create an error on exit helper. + ExitOnError(std::string Banner = "", int DefaultErrorExitCode = 1) + : Banner(std::move(Banner)), + GetExitCode([=](const Error &) { return DefaultErrorExitCode; }) {} + + /// Set the banner string for any errors caught by operator(). + void setBanner(std::string Banner) { this->Banner = std::move(Banner); } + + /// Set the exit-code mapper function. + void setExitCodeMapper(std::function GetExitCode) { + this->GetExitCode = std::move(GetExitCode); + } + + /// Check Err. If it's in a failure state log the error(s) and exit. + void operator()(Error Err) const { checkError(std::move(Err)); } + + /// Check E. If it's in a success state then return the contained value. If + /// it's in a failure state log the error(s) and exit. + template T operator()(Expected &&E) const { + checkError(E.takeError()); + return std::move(*E); + } + + /// Check E. If it's in a success state then return the contained reference. If + /// it's in a failure state log the error(s) and exit. + template T& operator()(Expected &&E) const { + checkError(E.takeError()); + return *E; + } + +private: + void checkError(Error Err) const { + if (Err) { + int ExitCode = GetExitCode(Err); + logAllUnhandledErrors(std::move(Err), errs(), Banner); + exit(ExitCode); + } + } + + std::string Banner; + std::function GetExitCode; +}; + +} // end namespace wpi + +#endif // WPIUTIL_WPI_ERROR_H diff --git a/wpiutil/src/main/native/include/wpi/ErrorHandling.h b/wpiutil/src/main/native/include/wpi/ErrorHandling.h new file mode 100644 index 0000000000..ef5843ba48 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/ErrorHandling.h @@ -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 + +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 diff --git a/wpiutil/src/main/native/include/wpi/ErrorOr.h b/wpiutil/src/main/native/include/wpi/ErrorOr.h index 1a878d16a4..e1803fddae 100644 --- a/wpiutil/src/main/native/include/wpi/ErrorOr.h +++ b/wpiutil/src/main/native/include/wpi/ErrorOr.h @@ -24,18 +24,6 @@ namespace wpi { -/// Stores a reference that can be changed. -template -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 is a pointer-like class that represents the result of an @@ -71,7 +59,7 @@ class ErrorOr { static const bool isRef = std::is_reference::value; - using wrap = ReferenceStorage::type>; + using wrap = std::reference_wrapper::type>; public: using storage_type = typename std::conditional::type; diff --git a/wpiutil/src/main/native/include/wpi/FileSystem.h b/wpiutil/src/main/native/include/wpi/FileSystem.h index 626aaaab8e..668eea6325 100644 --- a/wpiutil/src/main/native/include/wpi/FileSystem.h +++ b/wpiutil/src/main/native/include/wpi/FileSystem.h @@ -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 #include @@ -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 => /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 ¤t_directory, - SmallVectorImpl &path); +void make_absolute(const Twine ¤t_directory, SmallVectorImpl &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 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 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 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 *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 +openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None, + SmallVectorImpl *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 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(); 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 status = State->CurrentEntry.status(); - if (!status) - ec = status.getError(); - } }; namespace detail { @@ -704,8 +937,15 @@ public: if (State->HasNoPushRequest) State->HasNoPushRequest = false; else { - ErrorOr 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 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] }; /// @} diff --git a/wpiutil/src/main/native/include/wpi/Hashing.h b/wpiutil/src/main/native/include/wpi/Hashing.h index 8ae30d1509..157b2b9a4c 100644 --- a/wpiutil/src/main/native/include/wpi/Hashing.h +++ b/wpiutil/src/main/native/include/wpi/Hashing.h @@ -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 #include #include -#include #include #include #include @@ -131,7 +133,7 @@ hash_code hash_value(const std::basic_string &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 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 std::enable_if::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(first); const char *s_end = reinterpret_cast(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. diff --git a/wpiutil/src/main/native/include/wpi/ManagedStatic.h b/wpiutil/src/main/native/include/wpi/ManagedStatic.h new file mode 100644 index 0000000000..b99ad66547 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/ManagedStatic.h @@ -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 +#include + +namespace wpi { + +/// object_creator - Helper method for ManagedStatic. +template struct object_creator { + static void *call() { return new C(); } +}; + +/// object_deleter - Helper method for ManagedStatic. +/// +template struct object_deleter { + static void call(void *Ptr) { delete (T *)Ptr; } +}; +template struct object_deleter { + 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 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 Deleter = object_deleter> +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(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(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 diff --git a/wpiutil/src/main/native/include/wpi/MathExtras.h b/wpiutil/src/main/native/include/wpi/MathExtras.h index 1ab43aa6ca..49f5e4e121 100644 --- a/wpiutil/src/main/native/include/wpi/MathExtras.h +++ b/wpiutil/src/main/native/include/wpi/MathExtras.h @@ -25,7 +25,15 @@ #include #ifdef _MSC_VER -#include +// Declare these intrinsics manually rather including intrin.h. It's very +// expensive, and MathExtras.h is popular. +// #include +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 { diff --git a/wpiutil/src/main/native/include/wpi/MemAlloc.h b/wpiutil/src/main/native/include/wpi/MemAlloc.h new file mode 100644 index 0000000000..56cb5df083 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/MemAlloc.h @@ -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 + +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 diff --git a/wpiutil/src/main/native/include/wpi/Path.h b/wpiutil/src/main/native/include/wpi/Path.h index 8f6236902d..26b19785d8 100644 --- a/wpiutil/src/main/native/include/wpi/Path.h +++ b/wpiutil/src/main/native/include/wpi/Path.h @@ -20,6 +20,7 @@ #include "wpi/iterator.h" #include #include +#include namespace wpi { namespace sys { @@ -360,22 +361,6 @@ void system_temp_directory(bool erasedOnReboot, SmallVectorImpl &result); /// @result True if a home directory is set, false otherwise. bool home_directory(SmallVectorImpl &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 //. -/// -/// @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 &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 &path, bool remove_dot_dot = false, Style style = Style::native); +#if defined(_WIN32) +std::error_code widenPath(const Twine &Path8, SmallVectorImpl &Path16); +#endif + } // end namespace path } // end namespace sys } // end namespace wpi diff --git a/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h b/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h index 90989540e2..fa136e005f 100644 --- a/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h +++ b/wpiutil/src/main/native/include/wpi/PointerLikeTypeTraits.h @@ -112,6 +112,39 @@ template <> struct PointerLikeTypeTraits { 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 +struct FunctionPointerLikeTypeTraits { + enum { NumLowBitsAvailable = detail::ConstantLog2::value }; + static inline void *getAsVoidPointer(FunctionPointerT P) { + assert((reinterpret_cast(P) & + ~((uintptr_t)-1 << NumLowBitsAvailable)) == 0 && + "Alignment not satisfied for an actual function pointer!"); + return reinterpret_cast(P); + } + static inline FunctionPointerT getFromVoidPointer(void *P) { + return reinterpret_cast(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 +struct PointerLikeTypeTraits + : FunctionPointerLikeTypeTraits<4, ReturnT (*)(ParamTs...)> {}; + } // end namespace wpi #endif diff --git a/wpiutil/src/main/native/include/wpi/STLExtras.h b/wpiutil/src/main/native/include/wpi/STLExtras.h index b020222f6e..e32a8571df 100644 --- a/wpiutil/src/main/native/include/wpi/STLExtras.h +++ b/wpiutil/src/main/native/include/wpi/STLExtras.h @@ -21,6 +21,7 @@ #include "wpi/iterator.h" #include "wpi/iterator_range.h" #include "wpi/optional.h" +#include "wpi/ErrorHandling.h" #include #include #include @@ -52,6 +53,29 @@ using ValueOfRange = typename std::remove_reference +//===----------------------------------------------------------------------===// + +template +struct negation : std::integral_constant {}; + +template struct conjunction : std::true_type {}; +template struct conjunction : B1 {}; +template +struct conjunction + : std::conditional, B1>::type {}; + +template struct make_const_ptr { + using type = + typename std::add_pointer::type>::type; +}; + +template struct make_const_ref { + using type = typename std::add_lvalue_reference< + typename std::add_const::type>::type; +}; + //===----------------------------------------------------------------------===// // Extra additions to //===----------------------------------------------------------------------===// @@ -176,6 +200,12 @@ void adl_swap(T &&lhs, T &&rhs) noexcept( adl_detail::adl_swap(std::forward(lhs), std::forward(rhs)); } +/// Test whether \p RangeOrContainer is empty. Similar to C++17 std::empty. +template +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 bool all_of(R &&range, UnaryPredicate P); +template +bool any_of(R &&range, UnaryPredicate P); template struct index_sequence; @@ -553,6 +585,132 @@ detail::zippy zip_first(T &&t, U &&u, std::forward(t), std::forward(u), std::forward(args)...); } +namespace detail { +template +static Iter next_or_end(const Iter &I, const Iter &End) { + if (I == End) + return End; + return std::next(I); +} + +template +static auto deref_or_none(const Iter &I, const Iter &End) + -> wpi::optional::type>::type> { + if (I == End) + return nullopt; + return *I; +} + +template struct ZipLongestItemType { + using type = + wpi::optional())>::type>::type>; +}; + +template struct ZipLongestTupleType { + using type = std::tuple::type...>; +}; + +template +class zip_longest_iterator + : public iterator_facade_base< + zip_longest_iterator, + typename std::common_type< + std::forward_iterator_tag, + typename std::iterator_traits::iterator_category...>::type, + typename ZipLongestTupleType::type, + typename std::iterator_traits>::type>::difference_type, + typename ZipLongestTupleType::type *, + typename ZipLongestTupleType::type> { +public: + using value_type = typename ZipLongestTupleType::type; + +private: + std::tuple iterators; + std::tuple end_iterators; + + template + bool test(const zip_longest_iterator &other, + index_sequence) const { + return wpi::any_of( + std::initializer_list{std::get(this->iterators) != + std::get(other.iterators)...}, + identity{}); + } + + template value_type deref(index_sequence) const { + return value_type( + deref_or_none(std::get(iterators), std::get(end_iterators))...); + } + + template + decltype(iterators) tup_inc(index_sequence) const { + return std::tuple( + next_or_end(std::get(iterators), std::get(end_iterators))...); + } + +public: + zip_longest_iterator(std::pair... ts) + : iterators(std::forward(ts.first)...), + end_iterators(std::forward(ts.second)...) {} + + value_type operator*() { return deref(index_sequence_for{}); } + + value_type operator*() const { return deref(index_sequence_for{}); } + + zip_longest_iterator &operator++() { + iterators = tup_inc(index_sequence_for{}); + return *this; + } + + bool operator==(const zip_longest_iterator &other) const { + return !test(other, index_sequence_for{}); + } +}; + +template class zip_longest_range { +public: + using iterator = + zip_longest_iterator()))...>; + 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 ts; + + template iterator begin_impl(index_sequence) const { + return iterator(std::make_pair(adl_begin(std::get(ts)), + adl_end(std::get(ts)))...); + } + + template iterator end_impl(index_sequence) const { + return iterator(std::make_pair(adl_end(std::get(ts)), + adl_end(std::get(ts)))...); + } + +public: + zip_longest_range(Args &&... ts_) : ts(std::forward(ts_)...) {} + + iterator begin() const { return begin_impl(index_sequence_for{}); } + iterator end() const { return end_impl(index_sequence_for{}); } +}; +} // 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 +detail::zip_longest_range zip_longest(T &&t, U &&u, + Args &&... args) { + return detail::zip_longest_range( + std::forward(t), std::forward(u), std::forward(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...> IterPairs; + std::tuple Begins; + std::tuple 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 bool incrementHelper() { - auto &IterPair = std::get(IterPairs); - if (IterPair.first == IterPair.second) + auto &Begin = std::get(Begins); + auto &End = std::get(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 ValueT *getHelper() const { - auto &IterPair = std::get(IterPairs); - if (IterPair.first == IterPair.second) + auto &Begin = std::get(Begins); + auto &End = std::get(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 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()); } 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 +struct on_first { + FuncTy func; + + template + 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 +auto size(R &&Range, typename std::enable_if< + std::is_same::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 @@ -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 +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 +auto upper_bound(R &&Range, ForwardIt I) -> decltype(adl_begin(Range)) { + return std::upper_bound(adl_begin(Range), adl_end(Range), I); +} + +template +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 +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 -auto size(R &&Range, typename std::enable_if< - std::is_same::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 //===----------------------------------------------------------------------===// @@ -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 +bool hasNItems( + IterTy &&Begin, IterTy &&End, unsigned N, + typename std::enable_if< + !std::is_same< + typename std::iterator_traits::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 +bool hasNItemsOrMore( + IterTy &&Begin, IterTy &&End, unsigned N, + typename std::enable_if< + !std::is_same< + typename std::iterator_traits::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 diff --git a/wpiutil/src/main/native/include/wpi/SmallSet.h b/wpiutil/src/main/native/include/wpi/SmallSet.h index 87fd287be3..86662ac661 100644 --- a/wpiutil/src/main/native/include/wpi/SmallSet.h +++ b/wpiutil/src/main/native/include/wpi/SmallSet.h @@ -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 #include #include +#include #include namespace wpi { +/// SmallSetIterator - This class implements a const_iterator for SmallSet by +/// delegating to the underlying SmallVector or Set iterators. +template +class SmallSetIterator + : public iterator_facade_base, + std::forward_iterator_tag, T> { +private: + using SetIterTy = typename std::set::const_iterator; + using VecIterTy = typename SmallVector::const_iterator; + using SelfTy = SmallSetIterator; + + /// 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::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 > 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; 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(); } diff --git a/wpiutil/src/main/native/include/wpi/SmallVector.h b/wpiutil/src/main/native/include/wpi/SmallVector.h index 3c5ad39491..93ab2b4457 100644 --- a/wpiutil/src/main/native/include/wpi/SmallVector.h +++ b/wpiutil/src/main/native/include/wpi/SmallVector.h @@ -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 #include @@ -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 struct SmallVectorAlignmentAndSize { + AlignedCharArrayUnion Base; + AlignedCharArrayUnion 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 class SmallVectorTemplateCommon : public SmallVectorBase { -private: - template 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; - 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(reinterpret_cast( + reinterpret_cast(this) + + offsetof(SmallVectorAlignmentAndSize, 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(&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 - This is where we put method /// implementations that are designed to work with non-POD-like T's. -template +template ::value> class SmallVectorTemplateBase : public SmallVectorTemplateCommon { protected: SmallVectorTemplateBase(size_t Size) : SmallVectorTemplateCommon(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 void SmallVectorTemplateBase::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(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(wpi::safe_malloc(NewCapacity*sizeof(T))); // Move the elements over. this->uninitialized_move(this->begin(), this->end(), NewElts); @@ -258,9 +265,8 @@ void SmallVectorTemplateBase::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(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(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 -class SmallVectorImpl : public SmallVectorTemplateBase::value> { - using SuperClass = SmallVectorTemplateBase::value>; +class SmallVectorImpl : public SmallVectorTemplateBase { + using SuperClass = SmallVectorTemplateBase; public: using iterator = typename SuperClass::iterator; @@ -338,8 +340,7 @@ public: protected: // Default ctor - Initialize to empty. explicit SmallVectorImpl(unsigned N) - : SmallVectorTemplateBase::value>(N*sizeof(T)) { - } + : SmallVectorTemplateBase::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 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 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(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 @@ -703,8 +690,8 @@ void SmallVectorImpl::swap(SmallVectorImpl &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::swap(SmallVectorImpl &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 &SmallVectorImpl:: this->destroy_range(NewEnd, this->end()); // Trim. - this->setEnd(NewEnd); + this->set_size(RHSSize); return *this; } @@ -766,7 +753,7 @@ SmallVectorImpl &SmallVectorImpl:: 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 &SmallVectorImpl:: this->begin()+CurSize); // Set end. - this->setEnd(this->begin()+RHSSize); + this->set_size(RHSSize); return *this; } @@ -793,8 +780,8 @@ SmallVectorImpl &SmallVectorImpl::operator=(SmallVectorImpl &&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 &SmallVectorImpl::operator=(SmallVectorImpl &&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 &SmallVectorImpl::operator=(SmallVectorImpl &&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 &SmallVectorImpl::operator=(SmallVectorImpl &&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 struct SmallVectorStorage { - typename SmallVectorTemplateCommon::U InlineElts[N - 1]; + AlignedCharArrayUnion InlineElts[N]; }; -template struct SmallVectorStorage {}; -template struct SmallVectorStorage {}; + +/// 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 struct alignas(alignof(T)) SmallVectorStorage {}; /// 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 struct SmallVectorStorage {}; /// Note that this does not attempt to be exception safe. /// template -class SmallVector : public SmallVectorImpl { - /// Inline space for elements which aren't stored in the base class. - SmallVectorStorage Storage; - +class SmallVector : public SmallVectorImpl, SmallVectorStorage { public: SmallVector() : SmallVectorImpl(N) {} diff --git a/wpiutil/src/main/native/include/wpi/StringExtras.h b/wpiutil/src/main/native/include/wpi/StringExtras.h index 10d6affc57..16d0db071f 100644 --- a/wpiutil/src/main/native/include/wpi/StringExtras.h +++ b/wpiutil/src/main/native/include/wpi/StringExtras.h @@ -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(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(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 Input) { - return toHex(toStringRef(Input)); +inline std::string toHex(ArrayRef 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 diff --git a/wpiutil/src/main/native/include/wpi/StringMap.h b/wpiutil/src/main/native/include/wpi/StringMap.h index 80110dc815..e6648ccd0b 100644 --- a/wpiutil/src/main/native/include/wpi/StringMap.h +++ b/wpiutil/src/main/native/include/wpi/StringMap.h @@ -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 #include #include @@ -163,7 +164,7 @@ public: size_t AllocSize = sizeof(StringMapEntry) + KeyLength + 1; StringMapEntry *NewItem = - static_cast(CheckedMalloc(AllocSize)); + static_cast(safe_malloc(AllocSize)); // Construct the value. new (NewItem) StringMapEntry(KeyLength, std::forward(InitVals)...); @@ -358,12 +359,6 @@ public: return try_emplace(KV.first, std::move(KV.second)); } - template - WPI_DEPRECATED("use try_emplace instead") - std::pair emplace_second(StringRef Key, ArgsTy &&... Args) { - return try_emplace(Key, std::forward(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 diff --git a/wpiutil/src/main/native/include/wpi/StringRef.h b/wpiutil/src/main/native/include/wpi/StringRef.h index 5c8729d574..c9e2e5d3c6 100644 --- a/wpiutil/src/main/native/include/wpi/StringRef.h +++ b/wpiutil/src/main/native/include/wpi/StringRef.h @@ -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 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 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 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 diff --git a/wpiutil/src/main/native/include/wpi/SwapByteOrder.h b/wpiutil/src/main/native/include/wpi/SwapByteOrder.h new file mode 100644 index 0000000000..50ad8324e6 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/SwapByteOrder.h @@ -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 +#include +#if defined(_MSC_VER) && !defined(_DEBUG) +#include +#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 +inline void swapByteOrder(T &Value) { + Value = getSwappedBytes(Value); +} + +} // end namespace sys +} // end namespace wpi + +#endif diff --git a/wpiutil/src/main/native/include/wpi/Twine.h b/wpiutil/src/main/native/include/wpi/Twine.h index bbf3a0fb6c..b227a42cdc 100644 --- a/wpiutil/src/main/native/include/wpi/Twine.h +++ b/wpiutil/src/main/native/include/wpi/Twine.h @@ -12,6 +12,7 @@ #include "wpi/SmallVector.h" #include "wpi/StringRef.h" +#include "wpi/ErrorHandling.h" #include #include #include diff --git a/wpiutil/src/main/native/include/wpi/VersionTuple.h b/wpiutil/src/main/native/include/wpi/VersionTuple.h new file mode 100644 index 0000000000..d37ee50cfa --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/VersionTuple.h @@ -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 +#include + +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 getMinor() const { + if (!HasMinor) + return nullopt; + return Minor; + } + + /// Retrieve the subminor version number, if provided. + optional getSubminor() const { + if (!HasSubminor) + return nullopt; + return Subminor; + } + + /// Retrieve the build version number, if provided. + optional 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 diff --git a/wpiutil/src/main/native/include/wpi/iterator.h b/wpiutil/src/main/native/include/wpi/iterator.h index 2bae588692..70bbdab9a0 100644 --- a/wpiutil/src/main/native/include/wpi/iterator.h +++ b/wpiutil/src/main/native/include/wpi/iterator.h @@ -202,9 +202,7 @@ template < typename ReferenceT = typename std::conditional< std::is_same::value_type>::value, - typename std::iterator_traits::reference, T &>::type, - // Don't provide these, they are mostly to act as aliases below. - typename WrappedTraitsT = std::iterator_traits> + typename std::iterator_traits::reference, T &>::type> class iterator_adaptor_base : public iterator_facade_base { @@ -288,7 +286,7 @@ template ())>::type> struct pointee_iterator : iterator_adaptor_base< - pointee_iterator, WrappedIteratorT, + pointee_iterator, WrappedIteratorT, typename std::iterator_traits::iterator_category, T> { pointee_iterator() = default; @@ -311,8 +309,10 @@ make_pointee_range(RangeT &&Range) { template ())> class pointer_iterator - : public iterator_adaptor_base, - WrappedIteratorT, T> { + : public iterator_adaptor_base< + pointer_iterator, WrappedIteratorT, + typename std::iterator_traits::iterator_category, + T> { mutable T Ptr; public: diff --git a/wpiutil/src/main/native/include/wpi/iterator_range.h b/wpiutil/src/main/native/include/wpi/iterator_range.h index e43c1f73c0..78a60c2f7c 100644 --- a/wpiutil/src/main/native/include/wpi/iterator_range.h +++ b/wpiutil/src/main/native/include/wpi/iterator_range.h @@ -59,9 +59,10 @@ template iterator_range make_range(std::pair p) { return iterator_range(std::move(p.first), std::move(p.second)); } -template -iterator_range()))> drop_begin(T &&t, int n) { - return make_range(std::next(begin(t), n), end(t)); +template +iterator_range()))> drop_begin(T &&t, + int n) { + return make_range(std::next(adl_begin(t), n), adl_end(t)); } } diff --git a/wpiutil/src/main/native/include/wpi/memory.h b/wpiutil/src/main/native/include/wpi/memory.h deleted file mode 100644 index 325b5d7f61..0000000000 --- a/wpiutil/src/main/native/include/wpi/memory.h +++ /dev/null @@ -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 - -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_ diff --git a/wpiutil/src/main/native/include/wpi/raw_ostream.h b/wpiutil/src/main/native/include/wpi/raw_ostream.h index 9e14d6dbdb..bb577fee5d 100644 --- a/wpiutil/src/main/native/include/wpi/raw_ostream.h +++ b/wpiutil/src/main/native/include/wpi/raw_ostream.h @@ -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 Buffer; + virtual void anchor() override; + public: buffer_ostream(raw_ostream &OS) : raw_svector_ostream(Buffer), OS(OS) {} ~buffer_ostream() override { OS << str(); }