Resize a FlatBuffer in-place by iterating through all offsets in the buffer and adjusting them by "delta" if they straddle the start offset. Once that is done, bytes can now be inserted/deleted safely. "delta" may be negative (shrinking). Unless "delta" is a multiple of the largest alignment, you'll create a small amount of garbage space in the buffer (usually 0..7 bytes). If your FlatBuffer's roo
| 175 | // If your FlatBuffer's root table is not the schema's root table, you should |
| 176 | // pass in your root_table type as well. |
| 177 | class ResizeContext { |
| 178 | public: |
| 179 | ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta, |
| 180 | std::vector<uint8_t> *flatbuf, |
| 181 | const reflection::Object *root_table = nullptr) |
| 182 | : schema_(schema), |
| 183 | startptr_(vector_data(*flatbuf) + start), |
| 184 | delta_(delta), |
| 185 | buf_(*flatbuf), |
| 186 | dag_check_(flatbuf->size() / sizeof(uoffset_t), false) { |
| 187 | auto mask = static_cast<int>(sizeof(largest_scalar_t) - 1); |
| 188 | delta_ = (delta_ + mask) & ~mask; |
| 189 | if (!delta_) return; // We can't shrink by less than largest_scalar_t. |
| 190 | // Now change all the offsets by delta_. |
| 191 | auto root = GetAnyRoot(vector_data(buf_)); |
| 192 | Straddle<uoffset_t, 1>(vector_data(buf_), root, vector_data(buf_)); |
| 193 | ResizeTable(root_table ? *root_table : *schema.root_table(), root); |
| 194 | // We can now add or remove bytes at start. |
| 195 | if (delta_ > 0) |
| 196 | buf_.insert(buf_.begin() + start, delta_, 0); |
| 197 | else |
| 198 | buf_.erase(buf_.begin() + start + delta_, buf_.begin() + start); |
| 199 | } |
| 200 | |
| 201 | // Check if the range between first (lower address) and second straddles |
| 202 | // the insertion point. If it does, change the offset at offsetloc (of |
| 203 | // type T, with direction D). |
| 204 | template<typename T, int D> |
| 205 | void Straddle(const void *first, const void *second, void *offsetloc) { |
| 206 | if (first <= startptr_ && second >= startptr_) { |
| 207 | WriteScalar<T>(offsetloc, ReadScalar<T>(offsetloc) + delta_ * D); |
| 208 | DagCheck(offsetloc) = true; |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | // This returns a boolean that records if the corresponding offset location |
| 213 | // has been modified already. If so, we can't even read the corresponding |
| 214 | // offset, since it is pointing to a location that is illegal until the |
| 215 | // resize actually happens. |
| 216 | // This must be checked for every offset, since we can't know which offsets |
| 217 | // will straddle and which won't. |
| 218 | uint8_t &DagCheck(const void *offsetloc) { |
| 219 | auto dag_idx = reinterpret_cast<const uoffset_t *>(offsetloc) - |
| 220 | reinterpret_cast<const uoffset_t *>(vector_data(buf_)); |
| 221 | return dag_check_[dag_idx]; |
| 222 | } |
| 223 | |
| 224 | void ResizeTable(const reflection::Object &objectdef, Table *table) { |
| 225 | if (DagCheck(table)) return; // Table already visited. |
| 226 | auto vtable = table->GetVTable(); |
| 227 | // Early out: since all fields inside the table must point forwards in |
| 228 | // memory, if the insertion point is before the table we can stop here. |
| 229 | auto tableloc = reinterpret_cast<uint8_t *>(table); |
| 230 | if (startptr_ <= tableloc) { |
| 231 | // Check if insertion point is between the table and a vtable that |
| 232 | // precedes it. This can't happen in current construction code, but check |
| 233 | // just in case we ever change the way flatbuffers are built. |
| 234 | Straddle<soffset_t, -1>(vtable, table, table); |
no test coverage detected