Replace aos::SizedArray with absl::InlinedVector
Change-Id: I4dae096c5079afa3135e3a1cb7cb17e2789275e6
Signed-off-by: Tyler Chatow <tchatow@gmail.com>
diff --git a/aos/containers/BUILD b/aos/containers/BUILD
index e9f8262..9fdc93f 100644
--- a/aos/containers/BUILD
+++ b/aos/containers/BUILD
@@ -46,6 +46,9 @@
hdrs = [
"sized_array.h",
],
+ deps = [
+ "@com_google_absl//absl/container:inlined_vector",
+ ],
)
cc_test(
diff --git a/aos/containers/sized_array.h b/aos/containers/sized_array.h
index 4264cf6..8a8d2de 100644
--- a/aos/containers/sized_array.h
+++ b/aos/containers/sized_array.h
@@ -1,134 +1,28 @@
#ifndef AOS_CONTAINERS_SIZED_ARRAY_H_
#define AOS_CONTAINERS_SIZED_ARRAY_H_
-#include <array>
-#include <cstddef>
+#include "absl/container/inlined_vector.h"
namespace aos {
-// An array along with a variable size. This is a simple variable-size container
-// with a fixed maximum size.
-//
-// Note that it default-constructs N T instances at construction time. This
-// simplifies the internal bookkeeping a lot (I believe this can be
-// all-constexpr in C++17), but makes it a poor choice for complex T.
-template <typename T, size_t N>
-class SizedArray {
- private:
- using array = std::array<T, N>;
-
+// Minimal compliant allocator whose allocating operations are all fatal.
+template <typename T>
+class FatalAllocator {
public:
- using value_type = typename array::value_type;
- using size_type = typename array::size_type;
- using difference_type = typename array::difference_type;
- using reference = typename array::reference;
- using const_reference = typename array::const_reference;
- using pointer = typename array::pointer;
- using const_pointer = typename array::const_pointer;
- using iterator = typename array::iterator;
- using const_iterator = typename array::const_iterator;
- using reverse_iterator = typename array::reverse_iterator;
- using const_reverse_iterator = typename array::const_reverse_iterator;
+ using value_type = T;
- constexpr SizedArray() = default;
- SizedArray(const SizedArray &) = default;
- SizedArray(SizedArray &&) = default;
- SizedArray &operator=(const SizedArray &) = default;
- SizedArray &operator=(SizedArray &&) = default;
+ [[nodiscard, noreturn]] T *allocate(std::size_t) { __builtin_trap(); }
- bool operator==(const SizedArray &other) const {
- if (other.size() != size()) {
- return false;
- }
- for (size_t i = 0; i < size(); ++i) {
- if (other[i] != (*this)[i]) {
- return false;
- }
- }
- return true;
- }
- bool operator!=(const SizedArray &other) const {
- return !(*this == other);
- }
-
- reference at(size_t i) {
- check_index(i);
- return array_.at(i);
- }
- const_reference at(size_t i) const {
- check_index(i);
- return array_.at(i);
- }
-
- reference operator[](size_t i) { return array_[i]; }
- const_reference operator[](size_t i) const { return array_[i]; }
-
- reference front() { return array_.front(); }
- const_reference front() const { return array_.front(); }
-
- reference back() { return array_[size_ - 1]; }
- const_reference back() const { return array_[size_ - 1]; }
-
- T *data() { return array_.data(); }
- const T *data() const { return array_.data(); }
-
- iterator begin() { return array_.begin(); }
- const_iterator begin() const { return array_.begin(); }
- const_iterator cbegin() const { return array_.cbegin(); }
-
- iterator end() { return array_.begin() + size_; }
- const_iterator end() const { return array_.begin() + size_; }
- const_iterator cend() const { return array_.cbegin() + size_; }
-
- reverse_iterator rbegin() { return array_.rend() - size_; }
- const_reverse_iterator rbegin() const { return array_.rend() - size_; }
- const_reverse_iterator crbegin() const { return array_.crend() - size_; }
-
- reverse_iterator rend() { return array_.rend(); }
- const_reverse_iterator rend() const { return array_.rend(); }
- const_reverse_iterator crend() const { return array_.crend(); }
-
- bool empty() const { return size_ == 0; }
- bool full() const { return size() == max_size(); }
-
- size_t size() const { return size_; }
- constexpr size_t max_size() const { return array_.max_size(); }
-
- void push_back(const T &t) {
- array_.at(size_) = t;
- ++size_;
- }
- void push_back(T &&t) {
- array_.at(size_) = std::move(t);
- ++size_;
- }
-
- void pop_back() {
- if (empty()) {
- __builtin_trap();
- }
- --size_;
- }
-
- void clear() { size_ = 0; }
-
- // These allow access to the underlying storage. The data here may be outside
- // the current logical extents of the container.
- const array &backing_array() const { return array_; }
- array *mutable_backing_array() { return &array_; }
- void set_size(size_t size) { size_ = size; }
-
- private:
- void check_index(size_t i) const {
- if (i >= size_) {
- __builtin_trap();
- }
- }
-
- array array_;
- size_t size_ = 0;
+ [[noreturn]] void deallocate(T *, std::size_t) { __builtin_trap(); }
};
+// Reuse the logic from absl::InlinedVector for a statically allocated,
+// dynamically sized list of values. InlinedVector's default behavior is to
+// allocate from the heap when growing beyond the static capacity, make this
+// fatal instead to enforce RT guarantees.
+template <typename T, size_t N>
+using SizedArray = absl::InlinedVector<T, N, FatalAllocator<T>>;
+
} // namespace aos
#endif // AOS_CONTAINERS_SIZED_ARRAY_H_
diff --git a/aos/containers/sized_array_test.cc b/aos/containers/sized_array_test.cc
index bfaba06..d182ad2 100644
--- a/aos/containers/sized_array_test.cc
+++ b/aos/containers/sized_array_test.cc
@@ -32,39 +32,39 @@
TEST(SizedArrayTest, Accessors) {
SizedArray<int, 5> a;
EXPECT_TRUE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
EXPECT_EQ(0u, a.size());
- EXPECT_EQ(5u, a.max_size());
+ EXPECT_EQ(5u, a.capacity());
a.push_back(9);
EXPECT_FALSE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
EXPECT_EQ(1u, a.size());
- EXPECT_EQ(5u, a.max_size());
+ EXPECT_EQ(5u, a.capacity());
a.push_back(9);
EXPECT_FALSE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
EXPECT_EQ(2u, a.size());
- EXPECT_EQ(5u, a.max_size());
+ EXPECT_EQ(5u, a.capacity());
a.push_back(9);
EXPECT_FALSE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
EXPECT_EQ(3u, a.size());
- EXPECT_EQ(5u, a.max_size());
+ EXPECT_EQ(5u, a.capacity());
a.push_back(9);
EXPECT_FALSE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
EXPECT_EQ(4u, a.size());
- EXPECT_EQ(5u, a.max_size());
+ EXPECT_EQ(5u, a.capacity());
a.push_back(9);
EXPECT_FALSE(a.empty());
- EXPECT_TRUE(a.full());
+ EXPECT_EQ(a.size(), a.capacity());
EXPECT_EQ(5u, a.size());
- EXPECT_EQ(5u, a.max_size());
+ EXPECT_EQ(5u, a.capacity());
}
// Tests the various kinds of iterator.
@@ -139,20 +139,35 @@
TEST(SizedArrayTest, FillEmpty) {
SizedArray<int, 2> a;
EXPECT_TRUE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
a.push_back(9);
EXPECT_FALSE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
a.push_back(7);
EXPECT_FALSE(a.empty());
- EXPECT_TRUE(a.full());
+ EXPECT_EQ(a.size(), a.capacity());
a.clear();
EXPECT_TRUE(a.empty());
- EXPECT_FALSE(a.full());
+ EXPECT_NE(a.size(), a.capacity());
a.push_back(1);
EXPECT_EQ(1, a.back());
}
+TEST(SizedArrayTest, OverflowTest) {
+ SizedArray<int, 4> a;
+ EXPECT_EQ(a.capacity(), 4u);
+ EXPECT_TRUE(a.empty());
+
+ const int* const pre_front = a.data();
+ a.assign({1, 2, 3, 4});
+
+ EXPECT_EQ(a.capacity(), 4u);
+ // Verify that we didn't reallocate
+ EXPECT_EQ(pre_front, a.data());
+
+ EXPECT_DEATH(a.emplace_back(5), "SIGILL");
+}
+
} // namespace testing
} // namespace aos
diff --git a/motors/peripheral/uart.cc b/motors/peripheral/uart.cc
index 6e48320..0617e3f 100644
--- a/motors/peripheral/uart.cc
+++ b/motors/peripheral/uart.cc
@@ -70,7 +70,7 @@
// otherwise reading S1) before the final one. In practice, the FIFOs are so
// short on this part it probably won't help anything.
aos::SizedArray<char, 4> result;
- while (DataAvailable() && !result.full()) {
+ while (DataAvailable() && result.size() != result.capacity()) {
result.push_back(ReadCharacter());
}
return result;
diff --git a/motors/print/usb.h b/motors/print/usb.h
index 1ed1bee..c2224d2 100644
--- a/motors/print/usb.h
+++ b/motors/print/usb.h
@@ -30,7 +30,7 @@
aos::SizedArray<char, 4> ReadStdin() override {
aos::SizedArray<char, 4> result;
- result.set_size(stdout_tty_->Read(result.data(), result.max_size()));
+ result.resize(stdout_tty_->Read(result.data(), result.capacity()));
return result;
}
@@ -58,7 +58,7 @@
aos::SizedArray<char, 4> ReadStdin() override {
aos::SizedArray<char, 4> result;
- result.set_size(stdout_tty_.Read(result.data(), result.max_size()));
+ result.resize(stdout_tty_.Read(result.data(), result.capacity()));
return result;
}
diff --git a/y2019/jevois/cobs.h b/y2019/jevois/cobs.h
index 979cc15..c429622 100644
--- a/y2019/jevois/cobs.h
+++ b/y2019/jevois/cobs.h
@@ -23,9 +23,8 @@
// output_buffer is where to store the result.
// Returns a span in output_buffer which has no 0 bytes.
template <size_t max_decoded_size>
-absl::Span<char> CobsEncode(
- absl::Span<const char> input,
- std::array<char, CobsMaxEncodedSize(max_decoded_size)> *output_buffer);
+absl::Span<char> CobsEncode(absl::Span<const char> input,
+ absl::Span<char> output_buffer);
// Decodes some COBS-encoded data.
// input is the data to decide. Its size may be at most
@@ -90,20 +89,19 @@
};
template <size_t max_decoded_size>
-absl::Span<char> CobsEncode(
- absl::Span<const char> input,
- std::array<char, CobsMaxEncodedSize(max_decoded_size)> *output_buffer) {
+absl::Span<char> CobsEncode(absl::Span<const char> input,
+ absl::Span<char> output_buffer) {
static_assert(max_decoded_size > 0, "Empty buffers not supported");
if (static_cast<size_t>(input.size()) > max_decoded_size) {
__builtin_trap();
}
auto input_pointer = input.begin();
- auto output_pointer = output_buffer->begin();
+ auto output_pointer = output_buffer.begin();
auto code_pointer = output_pointer;
++output_pointer;
uint8_t code = 1;
while (input_pointer < input.end()) {
- if (output_pointer >= output_buffer->end()) {
+ if (output_pointer >= output_buffer.end()) {
__builtin_trap();
}
if (*input_pointer == 0u) {
@@ -125,11 +123,11 @@
++input_pointer;
}
*code_pointer = code;
- if (output_pointer > output_buffer->end()) {
+ if (output_pointer > output_buffer.end()) {
__builtin_trap();
}
- return absl::Span<char>(*output_buffer)
- .subspan(0, output_pointer - output_buffer->begin());
+ return absl::Span<char>(output_buffer)
+ .subspan(0, output_pointer - output_buffer.begin());
}
template <size_t max_decoded_size>
diff --git a/y2019/jevois/cobs_test.cc b/y2019/jevois/cobs_test.cc
index fd312f0..fa4742f 100644
--- a/y2019/jevois/cobs_test.cc
+++ b/y2019/jevois/cobs_test.cc
@@ -32,8 +32,8 @@
template <size_t max_decoded_size>
void EncodeAndDecode(const absl::Span<const char> decoded_input) {
std::array<char, CobsMaxEncodedSize(max_decoded_size)> encoded_buffer;
- const auto encoded =
- CobsEncode<max_decoded_size>(decoded_input, &encoded_buffer);
+ const auto encoded = CobsEncode<max_decoded_size>(
+ decoded_input, absl::Span<char>(encoded_buffer));
ASSERT_LE(encoded.size(), encoded_buffer.size());
ASSERT_EQ(encoded.data(), &encoded_buffer.front());
diff --git a/y2019/jevois/serial.cc b/y2019/jevois/serial.cc
index 5febbb4..cd0bb99 100644
--- a/y2019/jevois/serial.cc
+++ b/y2019/jevois/serial.cc
@@ -1,13 +1,13 @@
#include "y2019/jevois/serial.h"
-#include "aos/logging/logging.h"
-
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <unistd.h>
+#include "aos/logging/logging.h"
+
namespace y2019 {
namespace jevois {
@@ -33,7 +33,7 @@
| IGNCR // ignore CR
| ICRNL // translate CR to newline on input
| IXON // disable XON/XOFF flow control on output
- );
+ );
// disable implementation-defined output processing
options.c_oflag &= ~OPOST;
@@ -42,7 +42,7 @@
| ICANON // disable cannonical mode
| ISIG // do not signal for INTR, QUIT, SUSP etc
| IEXTEN // disable platform dependent i/p processing
- );
+ );
cfsetispeed(&options, B115200);
cfsetospeed(&options, B115200);
diff --git a/y2019/jevois/serial.h b/y2019/jevois/serial.h
index 9dd4b0c..14e2f98 100644
--- a/y2019/jevois/serial.h
+++ b/y2019/jevois/serial.h
@@ -7,6 +7,6 @@
int open_via_terminos(const char *tty_name);
} // namespace jevois
-} // namespace frc971
+} // namespace y2019
#endif // Y2019_JEVOIS_SERIAL_H_
diff --git a/y2019/jevois/uart.cc b/y2019/jevois/uart.cc
index 2857bd6..9cded62 100644
--- a/y2019/jevois/uart.cc
+++ b/y2019/jevois/uart.cc
@@ -49,8 +49,9 @@
}
AOS_CHECK(remaining_space.empty());
UartToTeensyBuffer result;
- result.set_size(
- CobsEncode<uart_to_teensy_size()>(buffer, result.mutable_backing_array())
+ result.resize(result.capacity());
+ result.resize(
+ CobsEncode<uart_to_teensy_size()>(buffer, absl::Span<char>(result))
.size());
return result;
}
@@ -137,8 +138,9 @@
}
AOS_CHECK(remaining_space.empty());
UartToCameraBuffer result;
- result.set_size(
- CobsEncode<uart_to_camera_size()>(buffer, result.mutable_backing_array())
+ result.resize(result.capacity());
+ result.resize(
+ CobsEncode<uart_to_camera_size()>(buffer, absl::Span<char>(result))
.size());
return result;
}
diff --git a/y2019/jevois/uart_test.cc b/y2019/jevois/uart_test.cc
index ff02f08..5a3a330 100644
--- a/y2019/jevois/uart_test.cc
+++ b/y2019/jevois/uart_test.cc
@@ -88,7 +88,7 @@
}
{
UartToTeensyBuffer buffer = UartPackToTeensy(input_message);
- buffer.set_size(buffer.size() - 1);
+ buffer.resize(buffer.size() - 1);
EXPECT_FALSE(UartUnpackToTeensy(buffer));
}
{
@@ -113,7 +113,7 @@
}
{
UartToCameraBuffer buffer = UartPackToCamera(input_message);
- buffer.set_size(buffer.size() - 1);
+ buffer.resize(buffer.size() - 1);
EXPECT_FALSE(UartUnpackToCamera(buffer));
}
{
diff --git a/y2019/vision/target_sender.cc b/y2019/vision/target_sender.cc
index 4aa4b54..650693c 100644
--- a/y2019/vision/target_sender.cc
+++ b/y2019/vision/target_sender.cc
@@ -1,5 +1,3 @@
-#include "y2019/vision/target_finder.h"
-
#include <condition_variable>
#include <fstream>
#include <mutex>
@@ -16,19 +14,20 @@
#include "y2019/jevois/structures.h"
#include "y2019/jevois/uart.h"
#include "y2019/vision/image_writer.h"
+#include "y2019/vision/target_finder.h"
// This has to be last to preserve compatibility with other headers using AOS
// logging.
#include "glog/logging.h"
+using ::aos::monotonic_clock;
using ::aos::events::DataSocket;
using ::aos::events::RXUdpSocket;
using ::aos::events::TCPServer;
using ::aos::vision::DataRef;
using ::aos::vision::Int32Codec;
-using ::aos::monotonic_clock;
-using ::y2019::jevois::open_via_terminos;
using aos::vision::Segment;
+using ::y2019::jevois::open_via_terminos;
class CameraStream : public ::y2019::camera::ImageStreamEvent {
public:
@@ -41,8 +40,9 @@
if (on_frame_) on_frame_(data, monotonic_now);
}
- void set_on_frame(const std::function<
- void(DataRef, monotonic_clock::time_point)> &on_frame) {
+ void set_on_frame(
+ const std::function<void(DataRef, monotonic_clock::time_point)>
+ &on_frame) {
on_frame_ = on_frame;
}
@@ -67,12 +67,12 @@
exit(-1);
}
+using aos::vision::ImageFormat;
using aos::vision::ImageRange;
using aos::vision::RangeImage;
-using aos::vision::ImageFormat;
-using y2019::vision::TargetFinder;
using y2019::vision::IntermediateResult;
using y2019::vision::Target;
+using y2019::vision::TargetFinder;
class TargetProcessPool {
public:
@@ -271,7 +271,7 @@
frc971::jevois::CameraFrame frame{};
- for (size_t i = 0; i < results.size() && i < frame.targets.max_size();
+ for (size_t i = 0; i < results.size() && i < frame.targets.capacity();
++i) {
const auto &result = results[i].extrinsics;
frame.targets.push_back(frc971::jevois::Target{
diff --git a/y2020/control_loops/superstructure/superstructure.cc b/y2020/control_loops/superstructure/superstructure.cc
index f70f107..b7d57b4 100644
--- a/y2020/control_loops/superstructure/superstructure.cc
+++ b/y2020/control_loops/superstructure/superstructure.cc
@@ -225,9 +225,8 @@
subsystems_not_ready.push_back(Subsystem::TURRET);
}
- subsystems_not_ready_offset =
- status->fbb()->CreateVector(subsystems_not_ready.backing_array().data(),
- subsystems_not_ready.size());
+ subsystems_not_ready_offset = status->fbb()->CreateVector(
+ subsystems_not_ready.data(), subsystems_not_ready.size());
}
Status::Builder status_builder = status->MakeBuilder<Status>();