53struct Line { int32_t
id = -1; };
54struct Box { int32_t
id = -1; };
55struct Label { int32_t
id = -1; };
100 if constexpr (std::is_same_v<Rec, LineRec>)
return "line";
101 else if constexpr (std::is_same_v<Rec, BoxRec>)
return "box";
102 else if constexpr (std::is_same_v<Rec, LabelRec>)
return "label";
103 else if constexpr (std::is_same_v<Rec, LinefillRec>)
return "linefill";
104 else static_assert(!std::is_same_v<Rec, Rec>,
"name the drawing kind of this record");
114 {{
"object",
object}}, what);
117 const char* function,
const char* argument,
const char* rule,
const char* what) {
120 {{
"function", function}, {
"argument", argument}, {
"rule", rule}}, what);
142 static_assert(std::is_copy_constructible_v<Rec> && std::is_nothrow_move_constructible_v<Rec>,
143 "a drawing record must be copyable and nothrow move-constructible");
144 static constexpr unsigned kBits = 5;
145 static constexpr std::uint32_t kWidth = std::uint32_t{1} << kBits;
146 static constexpr std::uint32_t kMask = kWidth - 1;
156 std::uint32_t
used = 0;
160 Block(
const Block& other) {
162 for (;
used < other.used; ++
used) ::new (&slots[
used].rec) Rec(other.slots[
used].rec);
168 Block& operator=(
const Block&) =
delete;
169 ~Block() { clear(); }
171 void clear() noexcept {
172 while (used > 0) slots[--used].rec.~Rec();
174 void append(Rec&& rec)
noexcept {
175 ::new (&slots[used].rec) Rec(std::move(rec));
179 struct Node { std::array<std::shared_ptr<void>, kWidth> kids{}; };
181 std::vector<Rec> flat_;
182 std::shared_ptr<Node> root_;
183 std::shared_ptr<Block> tail_;
184 std::uint32_t size_ = 0;
185 std::uint32_t tail_first_ = 0;
186 unsigned shift_ = kBits;
187 bool blocks_ =
false;
191 mutable std::atomic<bool> shared_{
false};
199 T* own(std::shared_ptr<T>& slot) {
200 if (!shared_.load(std::memory_order_relaxed))
return slot.get();
201 if (slot.use_count() != 1) slot = std::make_shared<T>(*slot);
202 else std::atomic_thread_fence(std::memory_order_acquire);
206 T* own_as(std::shared_ptr<void>& slot) {
207 if (!shared_.load(std::memory_order_relaxed))
return static_cast<T*
>(slot.get());
208 if (slot.use_count() != 1) slot = std::make_shared<T>(*
static_cast<const T*
>(slot.get()));
209 else std::atomic_thread_fence(std::memory_order_acquire);
210 return static_cast<T*
>(slot.get());
214 static std::shared_ptr<void> path_to(
unsigned level,
const std::shared_ptr<Block>& block) {
215 std::shared_ptr<void> node = block;
216 for (; level > 0; level -= kBits) {
217 auto parent = std::make_shared<Node>();
218 parent->kids[0] = std::move(node);
219 node = std::move(parent);
227 [[gnu::noinline, gnu::cold]]
const Rec& read_blocks(std::uint32_t
id)
const {
228 if (
id >= tail_first_)
return tail_->slots[
id & kMask].rec;
229 const void* node = root_.get();
230 for (
unsigned level = shift_; level > 0; level -= kBits)
231 node =
static_cast<const Node*
>(node)->kids[(
id >> level) & kMask].get();
232 return static_cast<const Block*
>(node)->slots[
id & kMask].rec;
235 [[gnu::noinline, gnu::cold]] Rec& write_blocks(std::uint32_t
id) {
236 if (!shared_.load(std::memory_order_relaxed))
return const_cast<Rec&
>(read_blocks(
id));
237 if (
id >= tail_first_)
return own(tail_)->slots[
id & kMask].rec;
238 Node* node = own(root_);
239 for (
unsigned level = shift_; level > kBits; level -= kBits)
240 node = own_as<Node>(node->kids[(
id >> level) & kMask]);
241 return own_as<Block>(node->kids[(
id >> kBits) & kMask])->slots[
id & kMask].rec;
247 [[gnu::noinline, gnu::cold]]
void push_block(Rec&& rec) {
248 if (size_ != 0 && size_ - tail_first_ < kWidth) {
249 own(tail_)->append(std::move(rec));
253 auto block = std::make_shared<Block>();
254 block->append(std::move(rec));
255 if (size_ != 0) push_tail();
256 tail_ = std::move(block);
264 const std::uint32_t last = size_ - 1;
266 auto root = std::make_shared<Node>();
267 root->kids[0] = tail_;
268 root_ = std::move(root);
272 if ((std::uint64_t{size_} >> kBits) > (std::uint64_t{1} << shift_)) {
273 auto root = std::make_shared<Node>();
274 root->kids[1] = path_to(shift_, tail_);
275 root->kids[0] = std::move(root_);
276 root_ = std::move(root);
280 Node* node = own(root_);
281 for (
unsigned level = shift_; level > kBits; level -= kBits) {
282 std::shared_ptr<void>& kid = node->kids[(last >> level) & kMask];
284 kid = path_to(level - kBits, tail_);
287 node = own_as<Node>(kid);
289 node->kids[(last >> kBits) & kMask] = tail_;
297 if (!other.blocks_) {
298 for (const Rec& rec : other.flat_) push_block(Rec(rec));
301 other.shared_.store(
true, std::memory_order_relaxed);
305 tail_first_ = other.tail_first_;
306 shift_ = other.shift_;
307 shared_.store(
true, std::memory_order_relaxed);
315 : flat_(std::move(other.flat_)), root_(std::move(other.root_)),
316 tail_(std::move(other.tail_)), size_(std::exchange(other.size_, 0)),
317 tail_first_(std::exchange(other.tail_first_, 0)),
318 shift_(std::exchange(other.shift_, kBits)), blocks_(std::exchange(other.blocks_,
false)),
319 shared_(other.shared_.exchange(
false, std::memory_order_relaxed)) {
323 if (
this != &other) {
324 flat_ = std::move(other.flat_);
326 root_ = std::move(other.root_);
327 tail_ = std::move(other.tail_);
328 size_ = std::exchange(other.size_, 0);
329 tail_first_ = std::exchange(other.tail_first_, 0);
330 shift_ = std::exchange(other.shift_, kBits);
331 blocks_ = std::exchange(other.blocks_,
false);
332 shared_.store(other.shared_.exchange(
false, std::memory_order_relaxed),
333 std::memory_order_relaxed);
338 int32_t
size()
const {
return static_cast<int32_t
>(size_); }
341 const Rec&
read(std::uint32_t
id)
const {
342 if (!blocks_)
return flat_[id];
343 return read_blocks(
id);
346 if (!blocks_)
return flat_[id];
347 return write_blocks(
id);
352 flat_.push_back(std::move(rec));
356 push_block(std::move(rec));
380 std::deque<int32_t> order_;
390 : recs_(
std::move(other.recs_)), order_(
std::move(other.order_)), cap_(other.cap_) {
391 other.order_.clear();
394 if (
this != &other) {
395 recs_ = std::move(other.recs_);
396 order_ = std::move(other.order_);
397 other.order_.clear();
404 while ((
int)order_.size() >= cap_) {
407 recs_.write(
static_cast<std::uint32_t
>(order_.front())).alive =
false;
410 int32_t
id = recs_.size();
412 recs_.push_back(std::move(r));
413 order_.push_back(
id);
418 if (!
alive(
id))
return;
419 recs_.write(
static_cast<std::uint32_t
>(
id)).alive =
false;
420 order_.erase(std::find(order_.begin(), order_.end(),
id));
424 return id >= 0 &&
id < recs_.size() && recs_.read(
static_cast<std::uint32_t
>(
id)).alive;
430 int32_t
size()
const {
return recs_.size(); }
435 Rec&
at(int32_t
id) {
return recs_.write(
static_cast<std::uint32_t
>(
id)); }
436 const Rec&
at(int32_t
id)
const {
return recs_.read(
static_cast<std::uint32_t
>(
id)); }
438 const std::deque<int32_t>&
order()
const {
return order_; }
448template <
class Rec,
class Handle>
450 if (h.id < 0 || h.id >= a.
size())
454template <
class Rec,
class Handle>
456 if (h.id < 0 || h.id >= a.
size())
475 p1.
price, p2.
price, xloc,
false,
false,
true }) };
508 const LineRec& r = std::as_const(a).at(h.
id);
511 "line.get_price requires xloc.bar_index");
513 return r.y1 + (r.y2 - r.y1) / (
double)(r.x2 - r.x1) * (
double)(x - r.x1);
526 return Box{ a.
alloc(
BoxRec{ left, right, top, bottom, xloc,
true }) };
531 top_left.
price, bottom_right.
price, xloc,
true }) };
561 BoxRec r = std::as_const(a).at(h.
id);
582 std::move(text),
true }) };
631template <
class... A>
inline void pf_noop(A&&...) {}
DrawingArena(const DrawingArena &)=default
DrawingArena & operator=(const DrawingArena &)=default
const std::deque< int32_t > & order() const
const Rec & at(int32_t id) const
DrawingArena & operator=(DrawingArena &&other) noexcept
bool alive(int32_t id) const
DrawingArena(DrawingArena &&other)
Base with a code: throw coded<std::invalid_argument>(code, {args}, text).
Rec & write(std::uint32_t id)
DrawingRecordStore & operator=(DrawingRecordStore &&other) noexcept
DrawingRecordStore()=default
DrawingRecordStore(DrawingRecordStore &&other) noexcept
DrawingRecordStore & operator=(const DrawingRecordStore &other)
const Rec & read(std::uint32_t id) const
DrawingRecordStore(const DrawingRecordStore &other)
void push_back(Rec &&rec)
void drawing_na_error(const char *object, const char *what)
void drawing_argument_error(const char *function, const char *argument, const char *rule, const char *what)
constexpr const char * drawing_object_name() noexcept
void pf_linefill_delete(DrawingArena< LinefillRec > &a, Linefill h)
void pf_line_set_xy1(DrawingArena< LineRec > &a, Line h, int64_t x, double y)
std::string pf_label_get_text(DrawingArena< LabelRec > &a, Label h)
void pf_label_delete(DrawingArena< LabelRec > &a, Label h)
void pf_line_set_xy2(DrawingArena< LineRec > &a, Line h, int64_t x, double y)
double pf_line_get_y1(DrawingArena< LineRec > &a, Line h)
void pf_box_set_bottom_right_point(DrawingArena< BoxRec > &a, Box h, ChartPoint p)
Line pf_linefill_get_line2(DrawingArena< LinefillRec > &a, Linefill h)
int64_t pf_point_x(const ChartPoint &p, XLoc xloc)
Line pf_line_copy(DrawingArena< LineRec > &a, Line h)
void pf_box_set_top(DrawingArena< BoxRec > &a, Box h, double v)
Box pf_box_new(DrawingArena< BoxRec > &a, int64_t left, double top, int64_t right, double bottom, XLoc xloc=XLoc::bar_index)
void pf_box_set_lefttop(DrawingArena< BoxRec > &a, Box h, int64_t left, double top)
void pf_label_set_point(DrawingArena< LabelRec > &a, Label h, ChartPoint p)
Box pf_box_new_pts(DrawingArena< BoxRec > &a, ChartPoint top_left, ChartPoint bottom_right, XLoc xloc=XLoc::bar_index)
void pf_box_set_right(DrawingArena< BoxRec > &a, Box h, int64_t v)
Line pf_line_new_pts(DrawingArena< LineRec > &a, ChartPoint p1, ChartPoint p2, XLoc xloc=XLoc::bar_index)
void pf_box_set_xloc(DrawingArena< BoxRec > &a, Box h, int64_t left, int64_t right, XLoc xloc)
void pf_box_set_top_left_point(DrawingArena< BoxRec > &a, Box h, ChartPoint p)
double pf_label_get_y(DrawingArena< LabelRec > &a, Label h)
Line pf_line_new(DrawingArena< LineRec > &a, int64_t x1, double y1, int64_t x2, double y2, XLoc xloc=XLoc::bar_index, bool el=false, bool er=false)
void pf_label_set_xy(DrawingArena< LabelRec > &a, Label h, int64_t x, double y)
void pf_line_set_y1(DrawingArena< LineRec > &a, Line h, double v)
Line pf_linefill_get_line1(DrawingArena< LinefillRec > &a, Linefill h)
Label pf_label_new(DrawingArena< LabelRec > &a, int64_t x, double y, std::string text, XLoc xloc=XLoc::bar_index, YLoc yloc=YLoc::price)
int64_t pf_box_get_left(DrawingArena< BoxRec > &a, Box h)
void pf_box_set_rightbottom(DrawingArena< BoxRec > &a, Box h, int64_t right, double bottom)
int64_t pf_line_get_x2(DrawingArena< LineRec > &a, Line h)
int64_t pf_line_get_x1(DrawingArena< LineRec > &a, Line h)
void pf_line_set_x2(DrawingArena< LineRec > &a, Line h, int64_t v)
double pf_box_get_bottom(DrawingArena< BoxRec > &a, Box h)
Linefill pf_linefill_new(DrawingArena< LinefillRec > &a, Line l1, Line l2)
void pf_label_set_x(DrawingArena< LabelRec > &a, Label h, int64_t v)
void pf_label_set_text(DrawingArena< LabelRec > &a, Label h, std::string text)
void pf_label_set_yloc(DrawingArena< LabelRec > &a, Label h, YLoc yloc)
bool is_na(const Line &h)
void pf_line_set_xloc(DrawingArena< LineRec > &a, Line h, int64_t x1, int64_t x2, XLoc xloc)
Rec & pf_require_live(DrawingArena< Rec > &a, Handle h)
void pf_line_set_first_point(DrawingArena< LineRec > &a, Line h, ChartPoint p)
double pf_box_get_top(DrawingArena< BoxRec > &a, Box h)
void pf_line_set_x1(DrawingArena< LineRec > &a, Line h, int64_t v)
Label pf_label_new_pt(DrawingArena< LabelRec > &a, ChartPoint point, std::string text, YLoc yloc=YLoc::price)
double pf_line_get_y2(DrawingArena< LineRec > &a, Line h)
int64_t pf_label_get_x(DrawingArena< LabelRec > &a, Label h)
void pf_box_delete(DrawingArena< BoxRec > &a, Box h)
void pf_line_set_second_point(DrawingArena< LineRec > &a, Line h, ChartPoint p)
void pf_line_delete(DrawingArena< LineRec > &a, Line h)
void pf_box_set_left(DrawingArena< BoxRec > &a, Box h, int64_t v)
int64_t pf_box_get_right(DrawingArena< BoxRec > &a, Box h)
double pf_line_get_price(DrawingArena< LineRec > &a, Line h, int64_t x)
void pf_line_set_y2(DrawingArena< LineRec > &a, Line h, double v)
void pf_label_set_y(DrawingArena< LabelRec > &a, Label h, double v)
Label pf_label_copy(DrawingArena< LabelRec > &a, Label h)
void pf_label_set_xloc(DrawingArena< LabelRec > &a, Label h, int64_t x, XLoc xloc)
void pf_box_set_bottom(DrawingArena< BoxRec > &a, Box h, double v)
Box pf_box_copy(DrawingArena< BoxRec > &a, Box h)
pine_drawing_error(const std::string &msg)