Skip to content

Commit 2f4b995

Browse files
qukhanxnnpack-bot
authored andcommitted
Change LockedBufferSpan to make it possible to create multiple spans from the same locked object.
PiperOrigin-RevId: 971629251
1 parent 073f3b0 commit 2f4b995

2 files changed

Lines changed: 72 additions & 22 deletions

File tree

litert/tensor/buffer.h

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,37 +54,38 @@ class LockedBufferSpan {
5454
using iterator = T*;
5555
using const_iterator = const T*;
5656

57-
// `const std::byte*` if `T` is `const`, otherwise `std::byte`.
58-
using MaybeConstByte =
59-
std::conditional_t<std::is_const_v<T>, const std::byte, std::byte>;
60-
61-
LockedBufferSpan(MaybeConstByte* data,
62-
std::function<void(MaybeConstByte*)> unlock, size_t bytes)
63-
: data_(data, std::move(unlock)), bytes_(bytes) {}
64-
65-
LockedBufferSpan(
66-
std::unique_ptr<MaybeConstByte, std::function<void(MaybeConstByte*)>>
67-
data,
68-
size_t bytes)
69-
: data_(std::move(data)), bytes_(bytes) {}
57+
template <class Unlock>
58+
LockedBufferSpan(T* data, Unlock&& unlock, size_t count)
59+
: data_(data, std::forward<Unlock>(unlock)), bytes_(count * sizeof(T)) {}
60+
61+
template <class Y, class Deleter>
62+
LockedBufferSpan(std::unique_ptr<Y, Deleter> data, size_t count)
63+
: data_(data.release(), data.get_deleter()), bytes_(count * sizeof(T)) {}
7064

7165
static LockedBufferSpan Empty() {
72-
return LockedBufferSpan(nullptr, [](MaybeConstByte*) {}, 0);
66+
return LockedBufferSpan(nullptr, [](T*) {}, 0);
7367
}
7468

7569
// Casts the span to a specific type.
76-
//
77-
// Warning: This transfers the lock management to the returned
78-
// `LockedBufferSpan`.
7970
template <class U>
80-
[[nodiscard]] LockedBufferSpan<U> As() && {
71+
[[nodiscard]] LockedBufferSpan<U> As() const {
8172
static_assert(
8273
std::is_const_v<U> || !std::is_const_v<T>,
8374
"Cannot cast from a constant buffer span to a non constant one.");
84-
return LockedBufferSpan<U>(std::move(data_), bytes_);
75+
return LockedBufferSpan<U>(data_, reinterpret_cast<U*>(data_.get()),
76+
bytes_ / sizeof(U));
77+
}
78+
79+
[[nodiscard]] LockedBufferSpan SubSpan(size_t offset,
80+
size_t count = SIZE_MAX) const {
81+
if (offset >= size()) {
82+
return Empty();
83+
}
84+
const size_t sub_count = std::min(count, size() - offset);
85+
return LockedBufferSpan(data_, data_.get() + offset, sub_count);
8586
}
8687

87-
T* data() const& { return reinterpret_cast<T*>(data_.get()); }
88+
T* data() const& { return data_.get(); }
8889
size_t size() const { return bytes_ / sizeof(T); }
8990
T* begin() & { return data(); }
9091
T* end() & { return data() + size(); }
@@ -102,8 +103,15 @@ class LockedBufferSpan {
102103
const T* cend() const&& = delete;
103104

104105
private:
105-
std::unique_ptr<MaybeConstByte, std::function<void(MaybeConstByte*)>> data_;
106-
size_t bytes_;
106+
template <class>
107+
friend class LockedBufferSpan;
108+
109+
template <class U>
110+
LockedBufferSpan(const std::shared_ptr<U>& data, T* ptr, size_t count)
111+
: data_(data, ptr), bytes_(count * sizeof(T)) {}
112+
113+
std::shared_ptr<T> data_;
114+
size_t bytes_ = 0;
107115
};
108116

109117
// The main interface for buffers.

litert/tensor/buffer_test.cc

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ using ::litert::tensor::IsOk;
4040
using ::testing::Address;
4141
using ::testing::Contains;
4242
using ::testing::Each;
43+
using ::testing::ElementsAre;
4344
using ::testing::ElementsAreArray;
4445
using ::testing::Eq;
4546
using ::testing::Not;
@@ -82,6 +83,47 @@ TEST(LockedBufferSpanTest, CanBeCastAndUsedAsAContainer) {
8283
SizeIs(std::size(Lockable::kData)));
8384
}
8485

86+
TEST(LockedBufferSpanTest, SharedOwnershipAcrossCopies) {
87+
Lockable l;
88+
ASSERT_EQ(l.i, 0);
89+
{
90+
LockedBufferSpan<std::byte> span1 = l.LockMutable();
91+
ASSERT_EQ(l.i, 1);
92+
{
93+
LockedBufferSpan<std::byte> span2 = span1;
94+
LockedBufferSpan<int> span3 = span1.As<int>();
95+
ASSERT_EQ(l.i, 1);
96+
}
97+
ASSERT_EQ(l.i, 1);
98+
}
99+
ASSERT_EQ(l.i, 0);
100+
}
101+
102+
TEST(LockedBufferSpanTest, SubSpan) {
103+
const int data[] = {10, 20, 30, 40, 50};
104+
LockedBufferSpan<const int> span(data, [](const int*) {}, std::size(data));
105+
106+
LockedBufferSpan<const int> sub1 = span.SubSpan(1, 3);
107+
EXPECT_THAT(sub1, ElementsAre(20, 30, 40));
108+
EXPECT_EQ(sub1.size(), 3);
109+
110+
LockedBufferSpan<const int> sub2 = span.SubSpan(2);
111+
EXPECT_THAT(sub2, ElementsAre(30, 40, 50));
112+
EXPECT_EQ(sub2.size(), 3);
113+
114+
LockedBufferSpan<const int> sub_oob = span.SubSpan(10);
115+
EXPECT_EQ(sub_oob.size(), 0);
116+
}
117+
118+
TEST(LockedBufferSpanTest, ConstructFromUniquePtr) {
119+
auto ptr = std::make_unique<int[]>(3);
120+
ptr[0] = 100;
121+
ptr[1] = 200;
122+
ptr[2] = 300;
123+
LockedBufferSpan<int> span(std::move(ptr), 3);
124+
EXPECT_THAT(span, ElementsAre(100, 200, 300));
125+
}
126+
85127
TEST(SpanCpuBufferTest, BuildFromRawData) {
86128
const int32_t backing_array[] = {1, 2, 3, 4, 5};
87129
SpanCpuBuffer bv(

0 commit comments

Comments
 (0)