Vince's CSV Parser
Loading...
Searching...
No Matches
data_frame_storage.hpp
1#pragma once
2
3#include <atomic>
4#include "dirty_data_frame.hpp"
5
6namespace csv {
7 namespace internals {
8 namespace data_frame {
17 public:
18 inline DataFrameStorage() : active_(&clean_) {}
19 DataFrameStorage(const DataFrameStorage&) = delete;
20 DataFrameStorage& operator=(const DataFrameStorage&) = delete;
21
23 : clean_(std::move(other.clean_)), dirty_(std::move(other.dirty_)), active_(&clean_) {
24 restore_choice();
25 other.active_.store(&other.clean_, std::memory_order_release);
26 }
27 inline DataFrameStorage& operator=(DataFrameStorage&& other) noexcept {
28 if (this != &other) {
29 clean_ = std::move(other.clean_);
30 dirty_ = std::move(other.dirty_);
31 restore_choice();
32 other.active_.store(&other.clean_, std::memory_order_release);
33 }
34 return *this;
35 }
36
37 // Both concrete backends use the same native row vector.
38 inline std::vector<CSVRow>& rows() noexcept { return clean_.rows(); }
39 inline const std::vector<CSVRow>& rows() const noexcept { return clean_.rows(); }
40 inline void set_names(ConstColNamesPtr names) { clean_.set_names(std::move(names)); }
41 inline const ConstColNamesPtr& names() const noexcept { return dispatch(Names()); }
42 inline size_t n_cols() const noexcept { return dispatch(Count()); }
43 inline CellBinding bind(size_t row, size_t column) const { return dispatch(Bind{row, column}); }
44 inline csv::string_view view(size_t row, size_t column) const { return dispatch(View{row, column}); }
45 inline CellBinding try_bind(size_t row, size_t column, CSVError& error) const {
46 error.clear();
47 if (row >= rows().size() || column >= n_cols()) {
48 fail_static(error, CSVErrorCode::IndexOutOfBounds, ERROR_COLUMN_INDEX_OUT_OF_RANGE);
49 return CellBinding{nullptr, 0};
50 }
51 const CellBinding binding = bind(row, column);
52 if (binding.physical_column >= rows()[row].size()) {
53 fail_static(error, CSVErrorCode::IndexOutOfBounds, CSV_ERROR_INDEX_OUT_OF_BOUNDS);
54 return CellBinding{nullptr, 0};
55 }
56 return binding;
57 }
58
59 inline const RowOverlay* find_edits(const CSVRow* row) const {
60 return dispatch(Edits{row_index(row)});
61 }
62 inline RowOverlay* ensure_edits(const CSVRow* row) {
63 return promote().ensure_edits(row_index(row));
64 }
65 inline CSVRow make_inserted_row(const std::vector<std::string>& row, CSVError& error) const {
66 return dispatch(MakeRow{row, error});
67 }
68 inline void reserve_insert() { dispatch_mutable(Reserve()); }
69 inline void insert_row(size_t index, CSVRow&& row) { dispatch_mutable(Insert{index, row}); }
70 inline void erase_row(size_t index) { dispatch_mutable(Erase{index}); }
71
72 inline bool erase_column(size_t index, int key_column, CSVError& error) {
73 error.clear();
74 if (index >= n_cols()) { return false; }
75 return promote().erase_column(index, key_column, error);
76 }
77
78 template<typename BeforeReplace>
79 inline bool insert_column(
80 size_t index, const std::string& name, const std::string& value,
81 int& key_column, const BeforeReplace& before_replace, CSVError& error
82 ) {
83 RowBackend* choice = active_.load(std::memory_order_acquire);
84 if (choice == &clean_) {
85 // Structural mutation is exclusive: this transient dirty backend
86 // needs no overlay slots and never becomes visible to cell readers.
87 DirtyDataFrame mutation(clean_, false);
88 if (!mutation.insert_column(index, name, value, key_column, before_replace, error)) return false;
89 }
90 else {
91 if (!static_cast<DirtyDataFrame*>(choice)->insert_column(index, name, value, key_column, before_replace, error)) return false;
92 active_.store(&clean_, std::memory_order_release);
93 dirty_.reset();
94 }
95 return true;
96 }
97
98 inline DataFrameStorage selected_rows(const std::vector<std::uint8_t>& mask, CSVError& error) const {
100 selected.clean_ = clean_.selected_rows(mask, error);
101 if (error) return selected;
102 const RowBackend* choice = active_.load(std::memory_order_acquire);
103 if (choice != &clean_) {
104 selected.dirty_ = static_cast<const DirtyDataFrame*>(choice)->selected_rows(selected.clean_, mask);
105 selected.active_.store(selected.dirty_.get(), std::memory_order_release);
106 }
107 return selected;
108 }
109
110 inline bool pristine() const noexcept {
111 return active_.load(std::memory_order_acquire) == &clean_ && rows().empty();
112 }
113
114 private:
115 template<typename Operation>
116 inline auto dispatch(const Operation& operation) const
117 -> decltype(operation(std::declval<const CleanDataFrame&>())) {
118 const RowBackend* choice = active_.load(std::memory_order_acquire);
119 if (choice == &clean_) { return operation(clean_); }
120 return operation(*static_cast<const DirtyDataFrame*>(choice));
121 }
122 template<typename Operation>
123 inline auto dispatch_mutable(const Operation& operation)
124 -> decltype(operation(std::declval<CleanDataFrame&>())) {
125 RowBackend* choice = active_.load(std::memory_order_acquire);
126 if (choice == &clean_) { return operation(clean_); }
127 return operation(*static_cast<DirtyDataFrame*>(choice));
128 }
129
130 inline DirtyDataFrame& promote() {
131 RowBackend* choice = active_.load(std::memory_order_acquire);
132 if (choice != &clean_) { return *static_cast<DirtyDataFrame*>(choice); }
133 std::lock_guard<std::mutex> lock(transition_lock_);
134 choice = active_.load(std::memory_order_acquire);
135 if (choice == &clean_) {
136 // Construct the schema and complete slot array before release.
137 dirty_.reset(new DirtyDataFrame(clean_));
138 choice = dirty_.get();
139 active_.store(choice, std::memory_order_release);
140 }
141 return *static_cast<DirtyDataFrame*>(choice);
142 }
143 inline size_t row_index(const CSVRow* row) const noexcept {
144 return static_cast<size_t>(row - rows().data());
145 }
146 inline void restore_choice() noexcept {
147 if (dirty_) {
148 dirty_->rebind(clean_);
149 active_.store(dirty_.get(), std::memory_order_release);
150 }
151 else { active_.store(&clean_, std::memory_order_release); }
152 }
153
154 struct Names { template<typename B> const ConstColNamesPtr& operator()(const B& b) const { return b.names(); } };
155 struct Count { template<typename B> size_t operator()(const B& b) const { return b.n_cols(); } };
156 struct Bind {
157 size_t row, column;
158 template<typename B> CellBinding operator()(const B& b) const { return b.bind(row, column); }
159 };
160 struct View {
161 size_t row, column;
162 template<typename B> csv::string_view operator()(const B& b) const { return b.view(row, column); }
163 };
164 struct Edits {
165 size_t row;
166 template<typename B> const RowOverlay* operator()(const B& b) const { return b.find_edits(row); }
167 };
168 struct MakeRow {
169 const std::vector<std::string>& row;
170 CSVError& error;
171 template<typename B> CSVRow operator()(const B& b) const { return b.make_inserted_row(row, error); }
172 };
173 struct Reserve { template<typename B> void operator()(B& b) const { b.reserve_insert(); } };
174 struct Insert {
175 size_t index; CSVRow& row;
176 template<typename B> void operator()(B& b) const { b.insert_row(index, std::move(row)); }
177 };
178 struct Erase {
179 size_t index;
180 template<typename B> void operator()(B& b) const { b.erase_row(index); }
181 };
182
183 CleanDataFrame clean_;
184 std::unique_ptr<DirtyDataFrame> dirty_;
185 std::mutex transition_lock_;
186 std::atomic<RowBackend*> active_;
187 };
188 }
189 }
190}
Data structure for representing CSV rows.
Definition csv_row.hpp:533
Borrow a row and error state for synchronous, single-pass field access.
Direct parsed-row backend and stable physical backing for editing.
C++11 tagged choice of concrete backends, without a vtable.
The all encompassing namespace.
@ IndexOutOfBounds
A row/column position exceeds the visible schema or physical record width.
std::string_view string_view
The string_view class used by this library.
Definition common.hpp:182
A diagnostic whose text remains valid for the lifetime of the error.
Definition csv_error.hpp:94