forked from OSchip/llvm-project
167 lines
4.9 KiB
C++
167 lines
4.9 KiB
C++
//===-- sanitizer_ring_buffer.h ---------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Simple ring buffer.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#ifndef SANITIZER_RING_BUFFER_H
|
|
#define SANITIZER_RING_BUFFER_H
|
|
|
|
#include "sanitizer_common.h"
|
|
|
|
namespace __sanitizer {
|
|
// RingBuffer<T>: fixed-size ring buffer optimized for speed of push().
|
|
// T should be a POD type and sizeof(T) should be divisible by sizeof(void*).
|
|
// At creation, all elements are zero.
|
|
template<class T>
|
|
class RingBuffer {
|
|
public:
|
|
COMPILER_CHECK(sizeof(T) % sizeof(void *) == 0);
|
|
static RingBuffer *New(uptr Size) {
|
|
void *Ptr = MmapOrDie(SizeInBytes(Size), "RingBuffer");
|
|
RingBuffer *RB = reinterpret_cast<RingBuffer*>(Ptr);
|
|
uptr End = reinterpret_cast<uptr>(Ptr) + SizeInBytes(Size);
|
|
RB->last_ = RB->next_ = reinterpret_cast<T*>(End - sizeof(T));
|
|
return RB;
|
|
}
|
|
void Delete() {
|
|
UnmapOrDie(this, SizeInBytes(size()));
|
|
}
|
|
uptr size() const {
|
|
return last_ + 1 -
|
|
reinterpret_cast<T *>(reinterpret_cast<uptr>(this) +
|
|
2 * sizeof(T *));
|
|
}
|
|
|
|
static uptr SizeInBytes(uptr Size) {
|
|
return Size * sizeof(T) + 2 * sizeof(T*);
|
|
}
|
|
|
|
uptr SizeInBytes() { return SizeInBytes(size()); }
|
|
|
|
void push(T t) {
|
|
*next_ = t;
|
|
next_--;
|
|
// The condition below works only if sizeof(T) is divisible by sizeof(T*).
|
|
if (next_ <= reinterpret_cast<T*>(&next_))
|
|
next_ = last_;
|
|
}
|
|
|
|
T operator[](uptr Idx) const {
|
|
CHECK_LT(Idx, size());
|
|
sptr IdxNext = Idx + 1;
|
|
if (IdxNext > last_ - next_)
|
|
IdxNext -= size();
|
|
return next_[IdxNext];
|
|
}
|
|
|
|
private:
|
|
RingBuffer() {}
|
|
~RingBuffer() {}
|
|
RingBuffer(const RingBuffer&) = delete;
|
|
|
|
// Data layout:
|
|
// LNDDDDDDDD
|
|
// D: data elements.
|
|
// L: last_, always points to the last data element.
|
|
// N: next_, initially equals to last_, is decremented on every push,
|
|
// wraps around if it's less or equal than its own address.
|
|
T *last_;
|
|
T *next_;
|
|
T data_[1]; // flexible array.
|
|
};
|
|
|
|
// A ring buffer with externally provided storage that encodes its state in 8
|
|
// bytes. Has significant constraints on size and alignment of storage.
|
|
// See a comment in hwasan/hwasan_thread_list.h for the motivation behind this.
|
|
#if SANITIZER_WORDSIZE == 64
|
|
template <class T>
|
|
class CompactRingBuffer {
|
|
// Top byte of long_ stores the buffer size in pages.
|
|
// Lower bytes store the address of the next buffer element.
|
|
static constexpr int kPageSizeBits = 12;
|
|
static constexpr int kSizeShift = 56;
|
|
static constexpr int kSizeBits = 64 - kSizeShift;
|
|
static constexpr uptr kNextMask = (1ULL << kSizeShift) - 1;
|
|
|
|
uptr GetStorageSize() const { return (long_ >> kSizeShift) << kPageSizeBits; }
|
|
|
|
static uptr SignExtend(uptr x) { return ((sptr)x) << kSizeBits >> kSizeBits; }
|
|
|
|
void Init(void *storage, uptr size) {
|
|
CHECK_EQ(sizeof(CompactRingBuffer<T>), sizeof(void *));
|
|
CHECK(IsPowerOfTwo(size));
|
|
CHECK_GE(size, 1 << kPageSizeBits);
|
|
CHECK_LE(size, 128 << kPageSizeBits);
|
|
CHECK_EQ(size % 4096, 0);
|
|
CHECK_EQ(size % sizeof(T), 0);
|
|
uptr st = (uptr)storage;
|
|
CHECK_EQ(st % (size * 2), 0);
|
|
CHECK_EQ(st, SignExtend(st & kNextMask));
|
|
long_ = (st & kNextMask) | ((size >> kPageSizeBits) << kSizeShift);
|
|
}
|
|
|
|
void SetNext(const T *next) {
|
|
long_ = (long_ & ~kNextMask) | ((uptr)next & kNextMask);
|
|
}
|
|
|
|
public:
|
|
CompactRingBuffer(void *storage, uptr size) {
|
|
Init(storage, size);
|
|
}
|
|
|
|
// A copy constructor of sorts.
|
|
CompactRingBuffer(const CompactRingBuffer &other, void *storage) {
|
|
uptr size = other.GetStorageSize();
|
|
internal_memcpy(storage, other.StartOfStorage(), size);
|
|
Init(storage, size);
|
|
uptr Idx = other.Next() - (const T *)other.StartOfStorage();
|
|
SetNext((const T *)storage + Idx);
|
|
}
|
|
|
|
T *Next() const { return (T *)(SignExtend(long_ & kNextMask)); }
|
|
|
|
void *StartOfStorage() const {
|
|
return (void *)((uptr)Next() & ~(GetStorageSize() - 1));
|
|
}
|
|
|
|
void *EndOfStorage() const {
|
|
return (void *)((uptr)StartOfStorage() + GetStorageSize());
|
|
}
|
|
|
|
uptr size() const { return GetStorageSize() / sizeof(T); }
|
|
|
|
void push(T t) {
|
|
T *next = Next();
|
|
*next = t;
|
|
next++;
|
|
next = (T *)((uptr)next & ~GetStorageSize());
|
|
SetNext(next);
|
|
}
|
|
|
|
const T &operator[](uptr Idx) const {
|
|
CHECK_LT(Idx, size());
|
|
const T *Begin = (const T *)StartOfStorage();
|
|
sptr StorageIdx = Next() - Begin;
|
|
StorageIdx -= (sptr)(Idx + 1);
|
|
if (StorageIdx < 0)
|
|
StorageIdx += size();
|
|
return Begin[StorageIdx];
|
|
}
|
|
|
|
public:
|
|
~CompactRingBuffer() {}
|
|
CompactRingBuffer(const CompactRingBuffer &) = delete;
|
|
|
|
uptr long_;
|
|
};
|
|
#endif
|
|
} // namespace __sanitizer
|
|
|
|
#endif // SANITIZER_RING_BUFFER_H
|