From 60dce66a4fc8049db292ce9c5e6bb48d0e92341e Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Sat, 29 Jun 2019 23:54:02 -0700 Subject: [PATCH] Remove wpi::ArrayRef std::initializer_list constructor (#1745) This can be dangerous as it refers to a temporary, and GCC 9.0 warns about its use. Instead add std::initializer_list overloads to common places it was used in an initializer_list sense. --- .../include/networktables/NetworkTableEntry.h | 150 +++++++++++++++++- .../networktables/NetworkTableEntry.inl | 68 +++++++- .../include/networktables/NetworkTableValue.h | 55 +++++++ wpilibc/src/main/native/cpp/LinearFilter.cpp | 3 +- .../cpp/filters/LinearDigitalFilter.cpp | 16 +- .../cpp/shuffleboard/RecordingController.cpp | 7 +- .../main/native/include/frc/LinearFilter.h | 12 ++ .../include/frc/filters/LinearDigitalFilter.h | 24 +++ wpiutil/src/main/native/cpp/WebSocket.cpp | 27 ++-- .../src/main/native/include/wpi/ArrayRef.h | 5 - .../src/main/native/include/wpi/WebSocket.h | 20 +++ .../main/native/include/wpi/WebSocketServer.h | 33 +++- .../src/main/native/include/wpi/uv/Stream.h | 56 ++++++- .../src/netconsoleServer/native/cpp/main.cpp | 18 ++- wpiutil/src/netconsoleTee/native/cpp/main.cpp | 18 ++- .../test/native/cpp/WebSocketClientTest.cpp | 5 +- .../test/native/cpp/WebSocketServerTest.cpp | 24 +-- 17 files changed, 482 insertions(+), 59 deletions(-) diff --git a/ntcore/src/main/native/include/networktables/NetworkTableEntry.h b/ntcore/src/main/native/include/networktables/NetworkTableEntry.h index 16b5c61d9c..8fdedc6e13 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableEntry.h +++ b/ntcore/src/main/native/include/networktables/NetworkTableEntry.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2017-2019 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. */ @@ -10,6 +10,7 @@ #include +#include #include #include #include @@ -176,6 +177,23 @@ class NetworkTableEntry final { */ std::vector GetBooleanArray(ArrayRef defaultValue) const; + /** + * Gets the entry's value as a boolean array. If the entry does not exist + * or is of different type, it will return the default value. + * + * @param defaultValue the value to be returned if no value is found + * @return the entry's value or the given default value + * + * @note This makes a copy of the array. If the overhead of this is a + * concern, use GetValue() instead. + * + * @note The returned array is std::vector instead of std::vector + * because std::vector is special-cased in C++. 0 is false, any + * non-zero value is true. + */ + std::vector GetBooleanArray( + std::initializer_list defaultValue) const; + /** * Gets the entry's value as a double array. If the entry does not exist * or is of different type, it will return the default value. @@ -188,6 +206,19 @@ class NetworkTableEntry final { */ std::vector GetDoubleArray(ArrayRef defaultValue) const; + /** + * Gets the entry's value as a double array. If the entry does not exist + * or is of different type, it will return the default value. + * + * @param defaultValue the value to be returned if no value is found + * @return the entry's value or the given default value + * + * @note This makes a copy of the array. If the overhead of this is a + * concern, use GetValue() instead. + */ + std::vector GetDoubleArray( + std::initializer_list defaultValue) const; + /** * Gets the entry's value as a string array. If the entry does not exist * or is of different type, it will return the default value. @@ -201,6 +232,19 @@ class NetworkTableEntry final { std::vector GetStringArray( ArrayRef defaultValue) const; + /** + * Gets the entry's value as a string array. If the entry does not exist + * or is of different type, it will return the default value. + * + * @param defaultValue the value to be returned if no value is found + * @return the entry's value or the given default value + * + * @note This makes a copy of the array. If the overhead of this is a + * concern, use GetValue() instead. + */ + std::vector GetStringArray( + std::initializer_list defaultValue) const; + /** * Sets the entry's value if it does not exist. * @@ -249,6 +293,14 @@ class NetworkTableEntry final { */ bool SetDefaultBooleanArray(ArrayRef defaultValue); + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ + bool SetDefaultBooleanArray(std::initializer_list defaultValue); + /** * Sets the entry's value if it does not exist. * @@ -257,6 +309,14 @@ class NetworkTableEntry final { */ bool SetDefaultDoubleArray(ArrayRef defaultValue); + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ + bool SetDefaultDoubleArray(std::initializer_list defaultValue); + /** * Sets the entry's value if it does not exist. * @@ -265,6 +325,14 @@ class NetworkTableEntry final { */ bool SetDefaultStringArray(ArrayRef defaultValue); + /** + * Sets the entry's value if it does not exist. + * + * @param defaultValue the default value to set + * @return False if the entry exists with a different type + */ + bool SetDefaultStringArray(std::initializer_list defaultValue); + /** * Sets the entry's value. * @@ -305,6 +373,22 @@ class NetworkTableEntry final { */ bool SetRaw(StringRef value); + /** + * Sets the entry's value. + * + * @param value the value to set + * @return False if the entry exists with a different type + */ + bool SetBooleanArray(ArrayRef value); + + /** + * Sets the entry's value. + * + * @param value the value to set + * @return False if the entry exists with a different type + */ + bool SetBooleanArray(std::initializer_list value); + /** * Sets the entry's value. * @@ -313,6 +397,14 @@ class NetworkTableEntry final { */ bool SetBooleanArray(ArrayRef value); + /** + * Sets the entry's value. + * + * @param value the value to set + * @return False if the entry exists with a different type + */ + bool SetBooleanArray(std::initializer_list value); + /** * Sets the entry's value. * @@ -321,6 +413,14 @@ class NetworkTableEntry final { */ bool SetDoubleArray(ArrayRef value); + /** + * Sets the entry's value. + * + * @param value the value to set + * @return False if the entry exists with a different type + */ + bool SetDoubleArray(std::initializer_list value); + /** * Sets the entry's value. * @@ -329,6 +429,14 @@ class NetworkTableEntry final { */ bool SetStringArray(ArrayRef value); + /** + * Sets the entry's value. + * + * @param value the value to set + * @return False if the entry exists with a different type + */ + bool SetStringArray(std::initializer_list value); + /** * Sets the entry's value. If the value is of different type, the type is * changed to match the new value. @@ -369,6 +477,22 @@ class NetworkTableEntry final { */ void ForceSetRaw(StringRef value); + /** + * Sets the entry's value. If the value is of different type, the type is + * changed to match the new value. + * + * @param value the value to set + */ + void ForceSetBooleanArray(ArrayRef value); + + /** + * Sets the entry's value. If the value is of different type, the type is + * changed to match the new value. + * + * @param value the value to set + */ + void ForceSetBooleanArray(std::initializer_list value); + /** * Sets the entry's value. If the value is of different type, the type is * changed to match the new value. @@ -377,6 +501,14 @@ class NetworkTableEntry final { */ void ForceSetBooleanArray(ArrayRef value); + /** + * Sets the entry's value. If the value is of different type, the type is + * changed to match the new value. + * + * @param value the value to set + */ + void ForceSetBooleanArray(std::initializer_list value); + /** * Sets the entry's value. If the value is of different type, the type is * changed to match the new value. @@ -385,6 +517,14 @@ class NetworkTableEntry final { */ void ForceSetDoubleArray(ArrayRef value); + /** + * Sets the entry's value. If the value is of different type, the type is + * changed to match the new value. + * + * @param value the value to set + */ + void ForceSetDoubleArray(std::initializer_list value); + /** * Sets the entry's value. If the value is of different type, the type is * changed to match the new value. @@ -393,6 +533,14 @@ class NetworkTableEntry final { */ void ForceSetStringArray(ArrayRef value); + /** + * Sets the entry's value. If the value is of different type, the type is + * changed to match the new value. + * + * @param value the value to set + */ + void ForceSetStringArray(std::initializer_list value); + /** * Sets flags. * diff --git a/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl b/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl index f95b1a8756..4a46b966c4 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl +++ b/ntcore/src/main/native/include/networktables/NetworkTableEntry.inl @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) FIRST 2017. All Rights Reserved. */ +/* Copyright (c) FIRST 2017-2019. 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. */ @@ -76,6 +76,12 @@ inline std::vector NetworkTableEntry::GetBooleanArray( return value->GetBooleanArray(); } +inline std::vector NetworkTableEntry::GetBooleanArray( + std::initializer_list defaultValue) const { + return GetBooleanArray( + wpi::makeArrayRef(defaultValue.begin(), defaultValue.end())); +} + inline std::vector NetworkTableEntry::GetDoubleArray( ArrayRef defaultValue) const { auto value = GetEntryValue(m_handle); @@ -83,6 +89,12 @@ inline std::vector NetworkTableEntry::GetDoubleArray( return value->GetDoubleArray(); } +inline std::vector NetworkTableEntry::GetDoubleArray( + std::initializer_list defaultValue) const { + return GetDoubleArray( + wpi::makeArrayRef(defaultValue.begin(), defaultValue.end())); +} + inline std::vector NetworkTableEntry::GetStringArray( ArrayRef defaultValue) const { auto value = GetEntryValue(m_handle); @@ -90,6 +102,12 @@ inline std::vector NetworkTableEntry::GetStringArray( return value->GetStringArray(); } +inline std::vector NetworkTableEntry::GetStringArray( + std::initializer_list defaultValue) const { + return GetStringArray( + wpi::makeArrayRef(defaultValue.begin(), defaultValue.end())); +} + inline bool NetworkTableEntry::SetDefaultValue(std::shared_ptr value) { return SetDefaultEntryValue(m_handle, value); } @@ -145,18 +163,42 @@ inline bool NetworkTableEntry::SetRaw(StringRef value) { return SetEntryValue(m_handle, Value::MakeRaw(value)); } +inline bool NetworkTableEntry::SetBooleanArray(ArrayRef value) { + return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); +} + +inline bool NetworkTableEntry::SetBooleanArray( + std::initializer_list value) { + return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); +} + inline bool NetworkTableEntry::SetBooleanArray(ArrayRef value) { return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); } +inline bool NetworkTableEntry::SetBooleanArray( + std::initializer_list value) { + return SetEntryValue(m_handle, Value::MakeBooleanArray(value)); +} + inline bool NetworkTableEntry::SetDoubleArray(ArrayRef value) { return SetEntryValue(m_handle, Value::MakeDoubleArray(value)); } +inline bool NetworkTableEntry::SetDoubleArray( + std::initializer_list value) { + return SetEntryValue(m_handle, Value::MakeDoubleArray(value)); +} + inline bool NetworkTableEntry::SetStringArray(ArrayRef value) { return SetEntryValue(m_handle, Value::MakeStringArray(value)); } +inline bool NetworkTableEntry::SetStringArray( + std::initializer_list value) { + return SetEntryValue(m_handle, Value::MakeStringArray(value)); +} + inline void NetworkTableEntry::ForceSetValue(std::shared_ptr value) { SetEntryTypeValue(m_handle, value); } @@ -177,19 +219,43 @@ inline void NetworkTableEntry::ForceSetRaw(StringRef value) { SetEntryTypeValue(m_handle, Value::MakeRaw(value)); } +inline void NetworkTableEntry::ForceSetBooleanArray(ArrayRef value) { + SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); +} + +inline void NetworkTableEntry::ForceSetBooleanArray( + std::initializer_list value) { + SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); +} + inline void NetworkTableEntry::ForceSetBooleanArray(ArrayRef value) { SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); } +inline void NetworkTableEntry::ForceSetBooleanArray( + std::initializer_list value) { + SetEntryTypeValue(m_handle, Value::MakeBooleanArray(value)); +} + inline void NetworkTableEntry::ForceSetDoubleArray(ArrayRef value) { SetEntryTypeValue(m_handle, Value::MakeDoubleArray(value)); } +inline void NetworkTableEntry::ForceSetDoubleArray( + std::initializer_list value) { + SetEntryTypeValue(m_handle, Value::MakeDoubleArray(value)); +} + inline void NetworkTableEntry::ForceSetStringArray( ArrayRef value) { SetEntryTypeValue(m_handle, Value::MakeStringArray(value)); } +inline void NetworkTableEntry::ForceSetStringArray( + std::initializer_list value) { + SetEntryTypeValue(m_handle, Value::MakeStringArray(value)); +} + inline void NetworkTableEntry::SetFlags(unsigned int flags) { SetEntryFlags(m_handle, GetFlags() | flags); } diff --git a/ntcore/src/main/native/include/networktables/NetworkTableValue.h b/ntcore/src/main/native/include/networktables/NetworkTableValue.h index 7b881afdab..1b8aabee2b 100644 --- a/ntcore/src/main/native/include/networktables/NetworkTableValue.h +++ b/ntcore/src/main/native/include/networktables/NetworkTableValue.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -374,6 +375,20 @@ class Value final { static std::shared_ptr MakeBooleanArray(ArrayRef value, uint64_t time = 0); + /** + * Creates a boolean array entry value. + * + * @param value the value + * @param time if nonzero, the creation time to use (instead of the current + * time) + * @return The entry value + */ + static std::shared_ptr MakeBooleanArray( + std::initializer_list value, uint64_t time = 0) { + return MakeBooleanArray(wpi::makeArrayRef(value.begin(), value.end()), + time); + } + /** * Creates a boolean array entry value. * @@ -385,6 +400,20 @@ class Value final { static std::shared_ptr MakeBooleanArray(ArrayRef value, uint64_t time = 0); + /** + * Creates a boolean array entry value. + * + * @param value the value + * @param time if nonzero, the creation time to use (instead of the current + * time) + * @return The entry value + */ + static std::shared_ptr MakeBooleanArray( + std::initializer_list value, uint64_t time = 0) { + return MakeBooleanArray(wpi::makeArrayRef(value.begin(), value.end()), + time); + } + /** * Creates a double array entry value. * @@ -396,6 +425,19 @@ class Value final { static std::shared_ptr MakeDoubleArray(ArrayRef value, uint64_t time = 0); + /** + * Creates a double array entry value. + * + * @param value the value + * @param time if nonzero, the creation time to use (instead of the current + * time) + * @return The entry value + */ + static std::shared_ptr MakeDoubleArray( + std::initializer_list value, uint64_t time = 0) { + return MakeDoubleArray(wpi::makeArrayRef(value.begin(), value.end()), time); + } + /** * Creates a string array entry value. * @@ -407,6 +449,19 @@ class Value final { static std::shared_ptr MakeStringArray(ArrayRef value, uint64_t time = 0); + /** + * Creates a string array entry value. + * + * @param value the value + * @param time if nonzero, the creation time to use (instead of the current + * time) + * @return The entry value + */ + static std::shared_ptr MakeStringArray( + std::initializer_list value, uint64_t time = 0) { + return MakeStringArray(wpi::makeArrayRef(value.begin(), value.end()), time); + } + /** * Creates a string array entry value. * diff --git a/wpilibc/src/main/native/cpp/LinearFilter.cpp b/wpilibc/src/main/native/cpp/LinearFilter.cpp index 5e7957e2de..28cd7c1750 100644 --- a/wpilibc/src/main/native/cpp/LinearFilter.cpp +++ b/wpilibc/src/main/native/cpp/LinearFilter.cpp @@ -26,8 +26,7 @@ LinearFilter LinearFilter::SinglePoleIIR(double timeConstant, double period) { LinearFilter LinearFilter::HighPass(double timeConstant, double period) { double gain = std::exp(-period / timeConstant); - const double ffGains[] = {gain, -gain}; - return LinearFilter(ffGains, -gain); + return LinearFilter({gain, -gain}, {-gain}); } LinearFilter LinearFilter::MovingAverage(int taps) { diff --git a/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp b/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp index db4cebe8fa..7dfc8f0ce6 100644 --- a/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp +++ b/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2015-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2015-2019 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. */ @@ -27,6 +27,13 @@ LinearDigitalFilter::LinearDigitalFilter(PIDSource& source, HAL_Report(HALUsageReporting::kResourceType_LinearFilter, instances); } +LinearDigitalFilter::LinearDigitalFilter(PIDSource& source, + std::initializer_list ffGains, + std::initializer_list fbGains) + : LinearDigitalFilter(source, + wpi::makeArrayRef(ffGains.begin(), ffGains.end()), + wpi::makeArrayRef(fbGains.begin(), fbGains.end())) {} + LinearDigitalFilter::LinearDigitalFilter(std::shared_ptr source, wpi::ArrayRef ffGains, wpi::ArrayRef fbGains) @@ -40,6 +47,13 @@ LinearDigitalFilter::LinearDigitalFilter(std::shared_ptr source, HAL_Report(HALUsageReporting::kResourceType_LinearFilter, instances); } +LinearDigitalFilter::LinearDigitalFilter(std::shared_ptr source, + std::initializer_list ffGains, + std::initializer_list fbGains) + : LinearDigitalFilter(source, + wpi::makeArrayRef(ffGains.begin(), ffGains.end()), + wpi::makeArrayRef(fbGains.begin(), fbGains.end())) {} + LinearDigitalFilter LinearDigitalFilter::SinglePoleIIR(PIDSource& source, double timeConstant, double period) { diff --git a/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp b/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp index 294be7979e..d80001e2a3 100644 --- a/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp +++ b/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2018-2019 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. */ @@ -44,7 +44,6 @@ void RecordingController::AddEventMarker( DriverStation::ReportError("Shuffleboard event name was not specified"); return; } - auto arr = wpi::ArrayRef{ - description, ShuffleboardEventImportanceName(importance)}; - m_eventsTable->GetSubTable(name)->GetEntry("Info").SetStringArray(arr); + m_eventsTable->GetSubTable(name)->GetEntry("Info").SetStringArray( + {description, ShuffleboardEventImportanceName(importance)}); } diff --git a/wpilibc/src/main/native/include/frc/LinearFilter.h b/wpilibc/src/main/native/include/frc/LinearFilter.h index 4f2bb9531d..8d71131ee5 100644 --- a/wpilibc/src/main/native/include/frc/LinearFilter.h +++ b/wpilibc/src/main/native/include/frc/LinearFilter.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -75,6 +76,17 @@ class LinearFilter { */ LinearFilter(wpi::ArrayRef ffGains, wpi::ArrayRef fbGains); + /** + * Create a linear FIR or IIR filter. + * + * @param ffGains The "feed forward" or FIR gains. + * @param fbGains The "feed back" or IIR gains. + */ + LinearFilter(std::initializer_list ffGains, + std::initializer_list fbGains) + : LinearFilter(wpi::makeArrayRef(ffGains.begin(), ffGains.end()), + wpi::makeArrayRef(fbGains.begin(), fbGains.end())) {} + LinearFilter(LinearFilter&&) = default; LinearFilter& operator=(LinearFilter&&) = default; diff --git a/wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h b/wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h index 274d72a456..dc365a1b74 100644 --- a/wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h +++ b/wpilibc/src/main/native/include/frc/filters/LinearDigitalFilter.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -81,6 +82,17 @@ class LinearDigitalFilter : public Filter { LinearDigitalFilter(PIDSource& source, wpi::ArrayRef ffGains, wpi::ArrayRef fbGains); + /** + * Create a linear FIR or IIR filter. + * + * @param source The PIDSource object that is used to get values + * @param ffGains The "feed forward" or FIR gains + * @param fbGains The "feed back" or IIR gains + */ + WPI_DEPRECATED("Use LinearFilter class instead.") + LinearDigitalFilter(PIDSource& source, std::initializer_list ffGains, + std::initializer_list fbGains); + /** * Create a linear FIR or IIR filter. * @@ -93,6 +105,18 @@ class LinearDigitalFilter : public Filter { wpi::ArrayRef ffGains, wpi::ArrayRef fbGains); + /** + * Create a linear FIR or IIR filter. + * + * @param source The PIDSource object that is used to get values + * @param ffGains The "feed forward" or FIR gains + * @param fbGains The "feed back" or IIR gains + */ + WPI_DEPRECATED("Use LinearFilter class instead.") + LinearDigitalFilter(std::shared_ptr source, + std::initializer_list ffGains, + std::initializer_list fbGains); + LinearDigitalFilter(LinearDigitalFilter&&) = default; LinearDigitalFilter& operator=(LinearDigitalFilter&&) = default; diff --git a/wpiutil/src/main/native/cpp/WebSocket.cpp b/wpiutil/src/main/native/cpp/WebSocket.cpp index dca081bec6..82c83bbf44 100644 --- a/wpiutil/src/main/native/cpp/WebSocket.cpp +++ b/wpiutil/src/main/native/cpp/WebSocket.cpp @@ -286,8 +286,9 @@ void WebSocket::SendClose(uint16_t code, const Twine& reason) { SmallVector bufs; if (code != 1005) { raw_uv_ostream os{bufs, 4096}; - os << ArrayRef{static_cast((code >> 8) & 0xff), - static_cast(code & 0xff)}; + const uint8_t codeMsb[] = {static_cast((code >> 8) & 0xff), + static_cast(code & 0xff)}; + os << ArrayRef(codeMsb); reason.print(os); } Send(kFlagFin | kOpClose, bufs, [](auto bufs, uv::Error) { @@ -520,18 +521,20 @@ void WebSocket::Send( os << static_cast((m_server ? 0x00 : kFlagMasking) | size); } else if (size <= 0xffff) { os << static_cast((m_server ? 0x00 : kFlagMasking) | 126); - os << ArrayRef{static_cast((size >> 8) & 0xff), - static_cast(size & 0xff)}; + const uint8_t sizeMsb[] = {static_cast((size >> 8) & 0xff), + static_cast(size & 0xff)}; + os << ArrayRef(sizeMsb); } else { os << static_cast((m_server ? 0x00 : kFlagMasking) | 127); - os << ArrayRef{static_cast((size >> 56) & 0xff), - static_cast((size >> 48) & 0xff), - static_cast((size >> 40) & 0xff), - static_cast((size >> 32) & 0xff), - static_cast((size >> 24) & 0xff), - static_cast((size >> 16) & 0xff), - static_cast((size >> 8) & 0xff), - static_cast(size & 0xff)}; + const uint8_t sizeMsb[] = {static_cast((size >> 56) & 0xff), + static_cast((size >> 48) & 0xff), + static_cast((size >> 40) & 0xff), + static_cast((size >> 32) & 0xff), + static_cast((size >> 24) & 0xff), + static_cast((size >> 16) & 0xff), + static_cast((size >> 8) & 0xff), + static_cast(size & 0xff)}; + os << ArrayRef(sizeMsb); } // clients need to mask the input data diff --git a/wpiutil/src/main/native/include/wpi/ArrayRef.h b/wpiutil/src/main/native/include/wpi/ArrayRef.h index 051a248018..c94312ebfd 100644 --- a/wpiutil/src/main/native/include/wpi/ArrayRef.h +++ b/wpiutil/src/main/native/include/wpi/ArrayRef.h @@ -98,11 +98,6 @@ namespace wpi { template /*implicit*/ constexpr ArrayRef(const T (&Arr)[N]) : Data(Arr), Length(N) {} - /// Construct an ArrayRef from a std::initializer_list. - /*implicit*/ ArrayRef(const std::initializer_list &Vec) - : Data(Vec.begin() == Vec.end() ? (T*)nullptr : Vec.begin()), - Length(Vec.size()) {} - /// Construct an ArrayRef from ArrayRef. This uses SFINAE to /// ensure that only ArrayRefs of pointers can be converted. template diff --git a/wpiutil/src/main/native/include/wpi/WebSocket.h b/wpiutil/src/main/native/include/wpi/WebSocket.h index b3c3cd50b9..8347d0cc83 100644 --- a/wpiutil/src/main/native/include/wpi/WebSocket.h +++ b/wpiutil/src/main/native/include/wpi/WebSocket.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -99,6 +100,25 @@ class WebSocket : public std::enable_shared_from_this { ArrayRef protocols = ArrayRef{}, const ClientOptions& options = ClientOptions{}); + /** + * Starts a client connection by performing the initial client handshake. + * An open event is emitted when the handshake completes. + * This sets the stream user data to the websocket. + * @param stream Connection stream + * @param uri The Request-URI to send + * @param host The host or host:port to send + * @param protocols The list of subprotocols + * @param options Handshake options + */ + static std::shared_ptr CreateClient( + uv::Stream& stream, const Twine& uri, const Twine& host, + std::initializer_list protocols, + const ClientOptions& options = ClientOptions{}) { + return CreateClient(stream, uri, host, + makeArrayRef(protocols.begin(), protocols.end()), + options); + } + /** * Starts a server connection by performing the initial server side handshake. * This should be called after the HTTP headers have been received. diff --git a/wpiutil/src/main/native/include/wpi/WebSocketServer.h b/wpiutil/src/main/native/include/wpi/WebSocketServer.h index c324c99b9a..a58a2c9eeb 100644 --- a/wpiutil/src/main/native/include/wpi/WebSocketServer.h +++ b/wpiutil/src/main/native/include/wpi/WebSocketServer.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2018-2019 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. */ @@ -9,6 +9,7 @@ #define WPIUTIL_WPI_WEBSOCKETSERVER_H_ #include +#include #include #include #include @@ -56,6 +57,20 @@ class WebSocketServerHelper { */ std::pair MatchProtocol(ArrayRef protocols); + /** + * Try to find a match to the list of sub-protocols provided by the client. + * The list is priority ordered, so the first match wins. + * Only valid during and after the upgrade event. + * @param protocols Acceptable protocols + * @return Pair; first item is true if a match was made, false if not. + * Second item is the matched protocol if a match was made, otherwise + * is empty. + */ + std::pair MatchProtocol( + std::initializer_list protocols) { + return MatchProtocol(makeArrayRef(protocols.begin(), protocols.end())); + } + /** * Accept the upgrade. Disconnect other readers (such as the HttpParser * reader) before calling this. See also WebSocket::CreateServer(). @@ -125,6 +140,22 @@ class WebSocketServer : public std::enable_shared_from_this { uv::Stream& stream, ArrayRef protocols = ArrayRef{}, const ServerOptions& options = ServerOptions{}); + /** + * Starts a dedicated WebSocket server on the provided connection. The + * connection should be an accepted client stream. + * This also sets the stream user data to the socket server. + * A connected event is emitted when the connection is opened. + * @param stream Connection stream + * @param protocols Acceptable subprotocols + * @param options Handshake options + */ + static std::shared_ptr Create( + uv::Stream& stream, std::initializer_list protocols, + const ServerOptions& options = ServerOptions{}) { + return Create(stream, makeArrayRef(protocols.begin(), protocols.end()), + options); + } + /** * Connected event. First parameter is the URL, second is the websocket. */ diff --git a/wpiutil/src/main/native/include/wpi/uv/Stream.h b/wpiutil/src/main/native/include/wpi/uv/Stream.h index 28e2ef707f..be8120df5d 100644 --- a/wpiutil/src/main/native/include/wpi/uv/Stream.h +++ b/wpiutil/src/main/native/include/wpi/uv/Stream.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2018-2019 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. */ @@ -11,6 +11,7 @@ #include #include +#include #include #include "wpi/ArrayRef.h" @@ -130,6 +131,27 @@ class Stream : public Handle { */ void Write(ArrayRef bufs, const std::shared_ptr& req); + /** + * Write data to the stream. + * + * Data are written in order. The lifetime of the data pointers passed in + * the `bufs` parameter must exceed the lifetime of the write request. + * An easy way to ensure this is to have the write request keep track of + * the data and use either its Complete() function or destructor to free the + * data. + * + * The finish signal will be emitted on the request object when the data + * has been written (or if an error occurs). + * The error signal will be emitted on the request object in case of errors. + * + * @param bufs The buffers to be written to the stream. + * @param req write request + */ + void Write(std::initializer_list bufs, + const std::shared_ptr& req) { + Write(makeArrayRef(bufs.begin(), bufs.end()), req); + } + /** * Write data to the stream. * @@ -146,6 +168,24 @@ class Stream : public Handle { void Write(ArrayRef bufs, std::function, Error)> callback); + /** + * Write data to the stream. + * + * Data are written in order. The lifetime of the data pointers passed in + * the `bufs` parameter must exceed the lifetime of the write request. + * The callback can be used to free data after the request completes. + * + * The callback will be called when the data has been written (even if an + * error occurred). Errors will be reported to the stream error handler. + * + * @param bufs The buffers to be written to the stream. + * @param callback Callback function to call when the write completes + */ + void Write(std::initializer_list bufs, + std::function, Error)> callback) { + Write(makeArrayRef(bufs.begin(), bufs.end()), callback); + } + /** * Queue a write request if it can be completed immediately. * @@ -158,6 +198,20 @@ class Stream : public Handle { */ int TryWrite(ArrayRef bufs); + /** + * Queue a write request if it can be completed immediately. + * + * Same as `Write()`, but won’t queue a write request if it can’t be + * completed immediately. + * An error signal will be emitted in case of errors. + * + * @param bufs The buffers to be written to the stream. + * @return Number of bytes written. + */ + int TryWrite(std::initializer_list bufs) { + return TryWrite(makeArrayRef(bufs.begin(), bufs.end())); + } + /** * Check if the stream is readable. * @return True if the stream is readable, false otherwise. diff --git a/wpiutil/src/netconsoleServer/native/cpp/main.cpp b/wpiutil/src/netconsoleServer/native/cpp/main.cpp index 0b08f24e1c..ed45eebe25 100644 --- a/wpiutil/src/netconsoleServer/native/cpp/main.cpp +++ b/wpiutil/src/netconsoleServer/native/cpp/main.cpp @@ -48,14 +48,16 @@ static bool NewlineBuffer(std::string& rem, uv::Buffer& buf, size_t len, // Header is 2 byte len, 1 byte type, 4 byte timestamp, 2 byte sequence num uint32_t ts = wpi::FloatToBits((wpi::Now() - startTime) * 1.0e-6); uint16_t len = rem.size() + toCopy.size() + 1 + 4 + 2; - out << wpi::ArrayRef({static_cast((len >> 8) & 0xff), - static_cast(len & 0xff), 12, - static_cast((ts >> 24) & 0xff), - static_cast((ts >> 16) & 0xff), - static_cast((ts >> 8) & 0xff), - static_cast(ts & 0xff), - static_cast((tcpSeq >> 8) & 0xff), - static_cast(tcpSeq & 0xff)}); + const uint8_t header[] = {static_cast((len >> 8) & 0xff), + static_cast(len & 0xff), + 12, + static_cast((ts >> 24) & 0xff), + static_cast((ts >> 16) & 0xff), + static_cast((ts >> 8) & 0xff), + static_cast(ts & 0xff), + static_cast((tcpSeq >> 8) & 0xff), + static_cast(tcpSeq & 0xff)}; + out << wpi::ArrayRef(header); } out << rem << toCopy; diff --git a/wpiutil/src/netconsoleTee/native/cpp/main.cpp b/wpiutil/src/netconsoleTee/native/cpp/main.cpp index 3c2b095c04..6c2a4fe204 100644 --- a/wpiutil/src/netconsoleTee/native/cpp/main.cpp +++ b/wpiutil/src/netconsoleTee/native/cpp/main.cpp @@ -39,14 +39,16 @@ static bool NewlineBuffer(std::string& rem, uv::Buffer& buf, size_t len, // Header is 2 byte len, 1 byte type, 4 byte timestamp, 2 byte sequence num uint32_t ts = wpi::FloatToBits((wpi::Now() - startTime) * 1.0e-6); uint16_t len = rem.size() + toCopy.size() + 1 + 4 + 2; - out << wpi::ArrayRef({static_cast((len >> 8) & 0xff), - static_cast(len & 0xff), 12, - static_cast((ts >> 24) & 0xff), - static_cast((ts >> 16) & 0xff), - static_cast((ts >> 8) & 0xff), - static_cast(ts & 0xff), - static_cast((tcpSeq >> 8) & 0xff), - static_cast(tcpSeq & 0xff)}); + const uint8_t header[] = {static_cast((len >> 8) & 0xff), + static_cast(len & 0xff), + 12, + static_cast((ts >> 24) & 0xff), + static_cast((ts >> 16) & 0xff), + static_cast((ts >> 8) & 0xff), + static_cast(ts & 0xff), + static_cast((tcpSeq >> 8) & 0xff), + static_cast(tcpSeq & 0xff)}; + out << wpi::ArrayRef(header); } out << rem << toCopy; diff --git a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp index afa123b664..2db9b54a55 100644 --- a/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp +++ b/wpiutil/src/test/native/cpp/WebSocketClientTest.cpp @@ -135,9 +135,8 @@ TEST_F(WebSocketClientTest, ProtocolGood) { mockProtocol = "myProtocol"; clientPipe->Connect(pipeName, [&] { - auto ws = WebSocket::CreateClient( - *clientPipe, "/test", pipeName, - ArrayRef{"myProtocol", "myProtocol2"}); + auto ws = WebSocket::CreateClient(*clientPipe, "/test", pipeName, + {"myProtocol", "myProtocol2"}); ws->closed.connect([&](uint16_t code, StringRef msg) { Finish(); if (code != 1005 && code != 1006) diff --git a/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp index a93436e421..d11fddad6e 100644 --- a/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp +++ b/wpiutil/src/test/native/cpp/WebSocketServerTest.cpp @@ -141,14 +141,15 @@ TEST_F(WebSocketServerTest, CloseCode) { }); }; // need to respond with close for server to finish shutdown - auto message = BuildMessage(0x08, true, true, {0x03u, 0xe8u}); + const uint8_t contents[] = {0x03u, 0xe8u}; + auto message = BuildMessage(0x08, true, true, contents); handleData = [&](StringRef) { clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {}); }; loop->Run(); - auto expectData = BuildMessage(0x08, true, false, {0x03u, 0xe8u}); + auto expectData = BuildMessage(0x08, true, false, contents); ASSERT_EQ(wireData, expectData); ASSERT_EQ(gotClosed, 1); } @@ -164,16 +165,15 @@ TEST_F(WebSocketServerTest, CloseReason) { }); }; // need to respond with close for server to finish shutdown - auto message = BuildMessage(0x08, true, true, - {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'}); + const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'}; + auto message = BuildMessage(0x08, true, true, contents); handleData = [&](StringRef) { clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {}); }; loop->Run(); - auto expectData = BuildMessage(0x08, true, false, - {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'}); + auto expectData = BuildMessage(0x08, true, false, contents); ASSERT_EQ(wireData, expectData); ASSERT_EQ(gotClosed, 1); } @@ -211,7 +211,8 @@ TEST_F(WebSocketServerTest, ReceiveCloseCode) { ASSERT_EQ(code, 1000) << "reason: " << reason; }); }; - auto message = BuildMessage(0x08, true, true, {0x03u, 0xe8u}); + const uint8_t contents[] = {0x03u, 0xe8u}; + auto message = BuildMessage(0x08, true, true, contents); resp.headersComplete.connect([&](bool) { clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {}); }); @@ -219,7 +220,7 @@ TEST_F(WebSocketServerTest, ReceiveCloseCode) { loop->Run(); // the endpoint should echo the message - auto expectData = BuildMessage(0x08, true, false, {0x03u, 0xe8u}); + auto expectData = BuildMessage(0x08, true, false, contents); ASSERT_EQ(wireData, expectData); ASSERT_EQ(gotClosed, 1); } @@ -233,8 +234,8 @@ TEST_F(WebSocketServerTest, ReceiveCloseReason) { ASSERT_EQ(reason, "hangup"); }); }; - auto message = BuildMessage(0x08, true, true, - {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'}); + const uint8_t contents[] = {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'}; + auto message = BuildMessage(0x08, true, true, contents); resp.headersComplete.connect([&](bool) { clientPipe->Write(uv::Buffer(message), [&](auto bufs, uv::Error) {}); }); @@ -242,8 +243,7 @@ TEST_F(WebSocketServerTest, ReceiveCloseReason) { loop->Run(); // the endpoint should echo the message - auto expectData = BuildMessage(0x08, true, false, - {0x03u, 0xe8u, 'h', 'a', 'n', 'g', 'u', 'p'}); + auto expectData = BuildMessage(0x08, true, false, contents); ASSERT_EQ(wireData, expectData); ASSERT_EQ(gotClosed, 1); }