├── LICENSE ├── Makefile ├── README.md ├── file_vector.hpp └── test.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Keean Schupke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all : test 2 | 3 | test: test.cpp file_vector.hpp 4 | clang++ -ggdb -march=native -O3 -flto -std=c++11 -lrt -o test test.cpp 5 | 6 | clean: 7 | rm -f test test1 test2 test3 test4 test5 test6 test7 test8 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | File-Vector 2 | =========== 3 | 4 | C++ file backed vector for very fast column databases, useful for time series data. The API is identical to the STL vector class, except a filename is provided to the constructor, which is created if necessary and mmaped to allow fast random access. Because the destructor can fail, one additional method (close) is provied which enables exceptions during closure to be caught. There is full write support including push_back, and file-space is reserved using the usual vector doubling algorithm. 5 | -------------------------------------------------------------------------------- /file_vector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILE_VECTOR_HPP 2 | #define FILE_VECTOR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern "C" { 10 | #include 11 | #include 12 | #include 13 | #include 14 | } 15 | 16 | using namespace std; 17 | 18 | // Use should be limited to data that does not contain pointers. 19 | // The template stops trivial pointers and references, 20 | // but not ones embedded in structs. 21 | 22 | template class file_vector; 23 | 24 | template 25 | class file_vector::value || is_reference::value)>::type> { 26 | using value_type = T; 27 | using reference = T&; 28 | using const_reference = T const&; 29 | using pointer = T*; 30 | using const_pointer = T const*; 31 | using difference_type = ptrdiff_t; 32 | using size_type = size_t; 33 | 34 | //------------------------------------------------------------------------ 35 | // Specialised private contructors and destructors for values 36 | 37 | template struct construct; 38 | 39 | template 40 | struct construct::value || is_pod::value>::type> { 41 | static void single(pointer value) {} 42 | static void single(pointer value, const_reference from) { 43 | *value = from; 44 | } 45 | static void many(pointer first, pointer last) {} 46 | static void many(pointer first, pointer last, const_reference from) { 47 | while (first < last) { 48 | *first++ = from; 49 | } 50 | } 51 | template 52 | static void forward(InputIterator first, InputIterator last, pointer dst) { 53 | copy(first, last, dst); 54 | } 55 | template 56 | static void backward(InputIterator first, InputIterator last, pointer dst) { 57 | copy_backward(first, last, dst); 58 | } 59 | }; 60 | 61 | template 62 | struct construct::value && !is_pod::value>::type> { 63 | static void single(pointer value) { 64 | new (static_cast(value)) value_type(); 65 | } 66 | static void single(pointer value, const_reference from) { 67 | new (static_cast(value)) value_type(from); 68 | } 69 | template 70 | static void single(pointer value, Args&&... args) { 71 | new (static_cast(value)) value_type(std::forward(args)...); 72 | } 73 | static void many(pointer first, pointer last) { 74 | while (first != last) { 75 | new (static_cast(first++)) value_type(); 76 | } 77 | } 78 | static void many(pointer first, pointer last, const_reference from) { 79 | while (first < last) { 80 | new (static_cast(first++)) value_type(from); 81 | } 82 | } 83 | template 84 | static void forward(InputIterator first, InputIterator last, pointer dst) { 85 | while (first < last) { 86 | new (static_cast(dst++)) value_type(*first++); 87 | } 88 | } 89 | template 90 | static void backward(InputIterator first, InputIterator last, pointer dst) { 91 | while (first < last) { 92 | new (static_cast(--dst)) value_type(*(--last)); 93 | } 94 | } 95 | }; 96 | 97 | template struct destroy; 98 | 99 | template 100 | struct destroy::value || is_pod::value>::type> { 101 | static void single(pointer value) {} 102 | static void many(pointer first, pointer last) {} 103 | }; 104 | 105 | template 106 | struct destroy::value && !is_pod::value>::type> { 107 | static void single(pointer value) { 108 | value->~value_type(); 109 | } 110 | static void many(pointer first, pointer last) { 111 | while (first < last) { 112 | first++->~value_type(); 113 | } 114 | } 115 | }; 116 | 117 | //------------------------------------------------------------------------ 118 | 119 | static size_type constexpr value_size = sizeof(T); 120 | 121 | int const mode; 122 | string const name; 123 | size_type reserved; 124 | size_type used; 125 | int fd; 126 | pointer values; 127 | 128 | void map_file_into_memory() { 129 | int flags = O_RDWR; 130 | if (mode & create_file) { 131 | flags |= O_CREAT; 132 | } 133 | fd = open(name.c_str(), flags, S_IRUSR | S_IWUSR); 134 | 135 | if (fd == -1) { 136 | throw runtime_error("Unable to open file for file_vector."); 137 | } 138 | 139 | size_type size = lseek(fd, 0, SEEK_END); 140 | 141 | if (size == -1) { 142 | if (::close(fd) == -1) { 143 | throw runtime_error("Unanble to close file after failing " 144 | "to get length of file for file_vector." 145 | ); 146 | } 147 | throw runtime_error("Unanble to get length of file for file_vector."); 148 | } 149 | 150 | used = size / value_size; 151 | reserved = size / value_size; 152 | 153 | // Posix does not allow mmap of zero size. 154 | if (reserved > 0) { 155 | values = static_cast(mmap(nullptr 156 | , reserved * value_size 157 | , PROT_READ | PROT_WRITE 158 | , MAP_SHARED 159 | , fd 160 | , 0 161 | )); 162 | 163 | if (values == nullptr) { 164 | if (::close(fd) == -1) { 165 | throw runtime_error("Unanble close file after failing " 166 | "to mmap file for file_vector." 167 | ); 168 | } 169 | throw runtime_error("Unable to mmap file for file_vector."); 170 | } 171 | } 172 | } 173 | 174 | //------------------------------------------------------------------------ 175 | // Resizes the file with ftruncate, and then maps the file into 176 | // memory at a new address. Because we are using shared mappings, this can 177 | // re-use the page-cache already in memory. Finally it unmaps the old 178 | // mapping leaving just the new 'resized' one, and points the class to the 179 | // new mapping. 180 | 181 | void resize_and_remap_file(size_type const size) { 182 | if (size == reserved) { 183 | return; 184 | } 185 | 186 | // First, resize the file. 187 | if (ftruncate(fd, size * value_size) == -1) { 188 | throw runtime_error("Unanble to extend memory for file_vector resize."); 189 | } 190 | 191 | // Second, map the resized file to a new address, sharing the elements. 192 | pointer new_values = nullptr; 193 | if (size > 0) { 194 | new_values = static_cast(mmap(nullptr 195 | , size * value_size 196 | , PROT_READ | PROT_WRITE 197 | , MAP_SHARED 198 | , fd 199 | , 0 200 | )); 201 | 202 | if (new_values == nullptr) { 203 | throw runtime_error("Unable to mmap file for file_vector resize."); 204 | } 205 | } 206 | 207 | // Third, unmap the file from the old address. 208 | if (reserved > 0) { 209 | if (values != nullptr && munmap(values, reserved * value_size) == -1) { 210 | if (new_values != nullptr && munmap(new_values, size) == -1) { 211 | throw runtime_error( 212 | "Unable to munmap file while " 213 | "handling failed munmap for file_vector." 214 | ); 215 | }; 216 | throw runtime_error("Unable to munmap file for file_vector resize."); 217 | } 218 | } 219 | 220 | // Finally, update the class. 221 | values = new_values; 222 | reserved = size; 223 | } 224 | 225 | size_type grow_to(size_type const size) { 226 | size_type capacity = reserved * 1.5; 227 | if (capacity < size) { 228 | capacity = size; 229 | } 230 | return capacity; 231 | } 232 | 233 | public: 234 | static int constexpr create_file = 1; 235 | 236 | void close() { 237 | if (values != nullptr) { 238 | if (munmap(values, reserved * value_size) == -1) { 239 | throw runtime_error("Unable to munmap file when closing file_vector."); 240 | } 241 | values = nullptr; 242 | } 243 | if (fd != -1) { 244 | if (ftruncate(fd, used * value_size) == -1) { 245 | throw runtime_error("Unable to resize file when closing file_vector."); 246 | } 247 | if (::close(fd) == -1) { 248 | throw runtime_error("Unable to close file when closing file_vector."); 249 | } 250 | fd = -1; 251 | } 252 | reserved = 0; 253 | used = 0; 254 | } 255 | 256 | virtual ~file_vector() noexcept { 257 | if (values != nullptr) { 258 | if (values != nullptr) { 259 | munmap(values, reserved); 260 | values = nullptr; 261 | } 262 | if (fd != -1) { 263 | if (ftruncate(fd, used * value_size) == -1) { 264 | // ignore. 265 | } 266 | ::close(fd); 267 | fd = -1; 268 | } 269 | reserved = 0; 270 | used = 0; 271 | } 272 | } 273 | 274 | //------------------------------------------------------------------------ 275 | // Vectors are provided with value identity, so vectors are equal if their 276 | // contents are equal, and assignment copies contents from one vector to 277 | // another, but does not assign the file name, so a vector file can 278 | // be copied and opened like this: 279 | // 280 | // file_vector dst_file("dst_file", file_vector("src_file")); 281 | 282 | file_vector(string const& name, int mode = 0) 283 | : mode(mode), name(name), reserved(0), used(0), fd(-1), values(nullptr) { 284 | map_file_into_memory(); 285 | } 286 | 287 | file_vector(string const& name, size_t n, int mode = 0) 288 | : mode(mode), name(name), reserved(0), used(0), fd(-1), values(nullptr) { 289 | map_file_into_memory(); 290 | assign(n); 291 | } 292 | 293 | file_vector(string const& name, size_t n, const_reference value, int mode = 0) 294 | : mode(mode), name(name), reserved(0), used(0), fd(-1), values(nullptr) { 295 | map_file_into_memory(); 296 | assign(n, value); 297 | } 298 | 299 | template 300 | file_vector(string const& name, InputIterator first, InputIterator last, int mode = 0) 301 | : mode(mode), name(name), reserved(0), used(0), fd(-1), values(nullptr) { 302 | assert (first <= last); 303 | 304 | map_file_into_memory(); 305 | assign(first, last); 306 | } 307 | 308 | file_vector(string const& name, file_vector const& from, int mode = 0) 309 | : file_vector(name, from.cbegin(), from.cend(), mode) {} 310 | 311 | file_vector(string const& name, file_vector&& from, int mode = 0) 312 | : file_vector(name, from.cbegin(), from.cend(), mode) {} 313 | 314 | file_vector(string const& name, initializer_list const& list, int mode = 0) 315 | : file_vector(name, list.begin(), list.end(), mode) {} 316 | 317 | file_vector(string const& name, vector const& src, int mode = 0) 318 | : file_vector(name, src.cbegin(), src.cend(), mode) {} 319 | 320 | // Value equality. 321 | bool operator== (file_vector const& that) const { 322 | return (used == that.size()) && range_same(that.cbegin(), that.cend(), cbegin()); 323 | } 324 | 325 | bool operator== (vector const& that) const { 326 | return (used == that.size()) && range_same(that.cbegin(), that.cend(), cbegin()); 327 | } 328 | 329 | // Copy contents. 330 | file_vector& operator= (file_vector const& src) { 331 | assign(src.cbegin(), src.cend()); 332 | return *this; 333 | } 334 | 335 | file_vector& operator= (vector const& src) { 336 | assign(src.cbegin(), src.cend()); 337 | return *this; 338 | } 339 | 340 | operator vector() { 341 | vector dst; 342 | dst.assign(begin(), end()); 343 | return dst; 344 | } 345 | 346 | //------------------------------------------------------------------------ 347 | // Iterator 348 | 349 | class iterator { 350 | public: 351 | using difference_type = file_vector::difference_type; 352 | using value_type = file_vector::value_type; 353 | using reference = file_vector::reference; 354 | using pointer = file_vector::pointer; 355 | using iterator_category = random_access_iterator_tag; 356 | 357 | private: 358 | friend file_vector; 359 | pointer values; 360 | 361 | iterator(pointer values) : values(values) 362 | {} 363 | 364 | public: 365 | // All Iterators 366 | iterator(iterator const &that) : values(that.values) 367 | {} 368 | iterator& operator= (iterator const &that) 369 | {values = that.values; return *this;} 370 | iterator& operator++ () 371 | {++values; return *this;} 372 | iterator operator++ (int) 373 | {iterator i {*this}; ++values; return i;} 374 | 375 | // Input 376 | bool operator== (iterator const &that) const 377 | {return values == that.values;} 378 | bool operator!= (iterator const &that) const 379 | {return values != that.values;} 380 | const_pointer operator-> () const 381 | {return values;} 382 | 383 | // Output 384 | reference operator* () const 385 | {return *values;} 386 | 387 | // Bidirectional 388 | iterator& operator-- () 389 | {--values; return *this;} 390 | iterator operator-- (int) 391 | {iterator i {*this}; --values; return i;} 392 | 393 | // Random Access 394 | difference_type operator- (iterator const &that) const 395 | {return values - that.values;} 396 | iterator operator- (difference_type const n) const 397 | {return iterator(values - n);} 398 | iterator operator+ (difference_type const n) const 399 | {return iterator(values + n);} 400 | bool operator< (iterator const &that) const 401 | {return values < that.values;} 402 | bool operator> (iterator const &that) const 403 | {return values > that.values;} 404 | bool operator<= (iterator const &that) const 405 | {return values <= that.values;} 406 | bool operator>= (iterator const &that) const 407 | {return values >= that.values;} 408 | iterator& operator += (difference_type const n) 409 | {values += n; return *this;} 410 | iterator& operator -= (difference_type const n) 411 | {values -= n; return *this;} 412 | reference operator[] (difference_type offset) const 413 | {return values[offset];} 414 | }; 415 | 416 | iterator begin() const { 417 | return iterator(values); 418 | } 419 | 420 | iterator end() const { 421 | return iterator(values + used); 422 | } 423 | 424 | class range { 425 | public: 426 | using iterator = file_vector::iterator; 427 | 428 | private: 429 | friend file_vector; 430 | range(iterator const &f, iterator const &l) : first(f), last(l) {} 431 | 432 | public: 433 | iterator const first; 434 | iterator const last; 435 | }; 436 | 437 | range all() const { 438 | return range(begin(), end()); 439 | } 440 | 441 | //------------------------------------------------------------------------ 442 | // Reverse Iterator 443 | 444 | class reverse_iterator { 445 | public: 446 | using difference_type = file_vector::difference_type; 447 | using value_type = file_vector::value_type; 448 | using reference = file_vector::reference; 449 | using pointer = file_vector::pointer; 450 | using iterator_category = random_access_iterator_tag; 451 | 452 | private: 453 | friend file_vector; 454 | file_vector::pointer values; 455 | 456 | reverse_iterator(file_vector::pointer values) : values(values) 457 | {} 458 | 459 | public: 460 | // All Iterators 461 | reverse_iterator(reverse_iterator const &that) : values(that.values) 462 | {} 463 | reverse_iterator& operator= (reverse_iterator const &that) 464 | {values = that.values; return *this;} 465 | reverse_iterator& operator++ () 466 | {--values; return *this;} 467 | reverse_iterator operator++ (int) 468 | {reverse_iterator i {*this}; --values; return i;} 469 | 470 | // Input 471 | bool operator== (reverse_iterator const &that) const 472 | {return values == that.values;} 473 | bool operator!= (reverse_iterator const &that) const 474 | {return values != that.values;} 475 | const_pointer operator-> () const 476 | {return values;} 477 | 478 | // Output 479 | reference operator* () const 480 | {return *values;} 481 | 482 | // Bidirectional 483 | reverse_iterator& operator-- () 484 | {++values; return *this;} 485 | reverse_iterator operator-- (int) 486 | {reverse_iterator i {*this}; ++values; return i;} 487 | 488 | // Random Access 489 | difference_type operator- (reverse_iterator const &that) 490 | {return that.values - values;} 491 | reverse_iterator operator- (difference_type const n) 492 | {return reverse_iterator(values + n);} 493 | reverse_iterator operator+ (difference_type const n) 494 | {return reverse_iterator(values - n);} 495 | bool operator< (reverse_iterator const &that) const 496 | {return values > that.values;} 497 | bool operator> (reverse_iterator const &that) const 498 | {return values < that.values;} 499 | bool operator<= (reverse_iterator const &that) const 500 | {return values >= that.values;} 501 | bool operator>= (reverse_iterator const &that) const 502 | {return values <= that.values;} 503 | reverse_iterator& operator += (difference_type const n) 504 | {values -= n; return *this;} 505 | reverse_iterator& operator -= (difference_type const n) 506 | {values += n; return *this;} 507 | reference& operator[] (difference_type offset) const 508 | {return values[offset];} 509 | }; 510 | 511 | reverse_iterator rbegin() const { 512 | return reverse_iterator(values + used - 1); 513 | } 514 | 515 | reverse_iterator rend() const { 516 | return reverse_iterator(values - 1); 517 | } 518 | 519 | class reverse_range { 520 | friend file_vector; 521 | reverse_range(reverse_iterator const &f, reverse_iterator const &l) : first(f), last(l) {} 522 | 523 | public: 524 | using iterator = file_vector::reverse_iterator; 525 | 526 | reverse_iterator const first; 527 | reverse_iterator const last; 528 | }; 529 | 530 | reverse_range reverse_all() const { 531 | return reverse_range(rbegin(), rend()); 532 | } 533 | 534 | //------------------------------------------------------------------------ 535 | // Constant Iterator 536 | 537 | class const_iterator { 538 | public: 539 | using difference_type = file_vector::difference_type; 540 | using value_type = file_vector::value_type; 541 | using reference = file_vector::const_reference; 542 | using pointer = file_vector::const_pointer; 543 | using iterator_category = random_access_iterator_tag; 544 | 545 | private: 546 | friend file_vector; 547 | const_pointer values; 548 | 549 | const_iterator(const_pointer values) : values(values) 550 | {} 551 | public: 552 | // All Iterators 553 | const_iterator(const_iterator const &that) : values(that.values) 554 | {} 555 | const_iterator& operator= (const_iterator const &that) 556 | {values = that.values; return *this;} 557 | const_iterator& operator++ () 558 | {++values; return *this;} 559 | const_iterator operator++ (int) 560 | {const_iterator i {*this}; ++values; return i;} 561 | 562 | // Input 563 | bool operator== (const_iterator const &that) const 564 | {return values == that.values;} 565 | bool operator!= (const_iterator const &that) const 566 | {return values != that.values;} 567 | const_pointer operator-> () const 568 | {return values;} 569 | const_reference operator* () const 570 | {return *values;} 571 | 572 | // Bidirectional 573 | const_iterator& operator-- () 574 | {--values; return *this;} 575 | const_iterator operator-- (int) 576 | {const_iterator i {*this}; --values; return i;} 577 | 578 | // Random Access 579 | difference_type operator- (const_iterator const &that) const 580 | {return values - that.values;} 581 | const_iterator operator- (difference_type const n) const 582 | {return const_iterator {values - n};} 583 | const_iterator operator+ (difference_type const n) const 584 | {return const_iterator {values + n};} 585 | bool operator< (const_iterator const &that) const 586 | {return values < that.values;} 587 | bool operator> (const_iterator const &that) const 588 | {return values > that.values;} 589 | bool operator<= (const_iterator const &that) const 590 | {return values <= that.values;} 591 | bool operator>= (const_iterator const &that) const 592 | {return values >= that.values;} 593 | const_iterator& operator += (difference_type const n) 594 | {values += n; return *this;} 595 | const_iterator& operator -= (difference_type const n) 596 | {values -= n; return *this;} 597 | const_reference operator[] (difference_type offset) const 598 | {return values[offset];} 599 | }; 600 | 601 | const_iterator cbegin() const { 602 | return const_iterator(values); 603 | } 604 | 605 | const_iterator cend() const { 606 | return const_iterator(values + used); 607 | } 608 | 609 | class const_range { 610 | friend file_vector; 611 | const_range(const_iterator const &f, const_iterator const &l) : first(f), last(l) {} 612 | 613 | public: 614 | using iterator = file_vector::const_iterator; 615 | 616 | const_iterator const first; 617 | const_iterator const last; 618 | }; 619 | 620 | const_range const_all() const { 621 | return const_range(cbegin(), cend()); 622 | } 623 | 624 | //------------------------------------------------------------------------ 625 | // Constant Reverse Iterator 626 | 627 | class const_reverse_iterator { 628 | public: 629 | using difference_type = file_vector::difference_type; 630 | using value_type = file_vector::value_type; 631 | using reference = file_vector::const_reference; 632 | using pointer = file_vector::const_pointer; 633 | using iterator_category = random_access_iterator_tag; 634 | 635 | private: 636 | friend file_vector; 637 | const_pointer values; 638 | 639 | const_reverse_iterator(const_pointer values) : values(values) 640 | {} 641 | 642 | public: 643 | // All Iterators 644 | const_reverse_iterator(const_reverse_iterator const &that) : values(that.values) 645 | {} 646 | const_reverse_iterator& operator= (const_reverse_iterator const &that) 647 | {values = that.values; return *this;} 648 | const_reverse_iterator& operator++ () 649 | {--values; return *this;} 650 | const_reverse_iterator operator++ (int) 651 | {const_reverse_iterator i {*this}; --values; return i;} 652 | 653 | // Input 654 | bool operator== (const_reverse_iterator const &that) const 655 | {return values == that.values;} 656 | bool operator!= (const_reverse_iterator const &that) const 657 | {return values != that.values;} 658 | const_pointer operator-> () const 659 | {return values;} 660 | const_reference operator* () const 661 | {return *values;} 662 | 663 | // Bidirectional 664 | const_reverse_iterator& operator-- () 665 | {++values; return *this;} 666 | const_reverse_iterator operator-- (int) 667 | {const_reverse_iterator i {*this}; ++values; return i;} 668 | 669 | // Random Access 670 | difference_type operator- (const_reverse_iterator const &that) const 671 | {return that.values - values;} 672 | const_reverse_iterator operator- (difference_type const n) const 673 | {return const_reverse_iterator {values + n};} 674 | const_reverse_iterator operator+ (difference_type const n) const 675 | {return const_reverse_iterator {values - n};} 676 | bool operator< (const_reverse_iterator const &that) const 677 | {return values > that.values;} 678 | bool operator> (const_reverse_iterator const &that) const 679 | {return values < that.values;} 680 | bool operator<= (const_reverse_iterator const &that) const 681 | {return values >= that.values;} 682 | bool operator>= (const_reverse_iterator const &that) const 683 | {return values <= that.values;} 684 | const_reverse_iterator& operator += (difference_type const n) 685 | {values -= n; return *this;} 686 | const_reverse_iterator& operator -= (difference_type const n) 687 | {values += n; return *this;} 688 | const_reference operator[] (difference_type offset) const 689 | {return values[offset];} 690 | }; 691 | 692 | const_reverse_iterator crbegin() const { 693 | return const_reverse_iterator(values + used - 1); 694 | } 695 | 696 | const_reverse_iterator crend() const { 697 | return const_reverse_iterator(values - 1); 698 | } 699 | 700 | class const_reverse_range { 701 | friend file_vector; 702 | const_reverse_range(const_reverse_iterator const &f, const_reverse_iterator const &l) 703 | : first(f), last(l) {} 704 | 705 | public: 706 | using iterator = file_vector::const_reverse_iterator; 707 | 708 | const_reverse_iterator const first; 709 | const_reverse_iterator const last; 710 | }; 711 | 712 | const_reverse_range const_reverse_all() const { 713 | return const_reverse_range(crbegin(), crend()); 714 | } 715 | 716 | //------------------------------------------------------------------------ 717 | 718 | template bool range_same(I first, I last, const_iterator with) const { 719 | assert(first < last); 720 | 721 | while (first != last) { 722 | if (*first++ != *with++) { 723 | return false; 724 | } 725 | } 726 | return true; 727 | } 728 | 729 | //------------------------------------------------------------------------ 730 | // Capacity 731 | 732 | size_type size() const { 733 | return used; 734 | } 735 | 736 | size_type capacity() const { 737 | return reserved; 738 | } 739 | 740 | // Resize so that the capacity is at least 'size', using a doubling 741 | // algorithm. 742 | void reserve(size_type const size) { 743 | if (used + size > reserved) { 744 | resize_and_remap_file(grow_to(used + size)); 745 | } 746 | } 747 | 748 | // Default construct or destroy values as necessary 749 | void resize(size_type const size) { 750 | if (size < used) { 751 | destroy::many(values + size, values + used); 752 | } else if (size > used) { 753 | reserve(size); 754 | construct::many(values + used, values + size); 755 | } 756 | 757 | used = size; 758 | } 759 | 760 | // Copy construct or destroy values as necessary 761 | void resize(size_type const size, const_reference value) { 762 | if (size < used) { 763 | destroy::many(values + size, values + used); 764 | } else if (size > used) { 765 | reserve(size - used); 766 | construct::many(values + used, values + size, value); 767 | } 768 | 769 | used = size; 770 | } 771 | 772 | bool empty() const { 773 | return used == 0; 774 | } 775 | 776 | void shrink_to_fit() { 777 | resize_and_remap_file(used); 778 | } 779 | 780 | //------------------------------------------------------------------------ 781 | // Element Access 782 | 783 | // Unchecked access 784 | const_reference operator[] (int const i) const { 785 | assert(0 <= i && i < used); 786 | 787 | return values[i]; 788 | } 789 | 790 | reference operator[] (int const i) { 791 | assert(0 <= i && i < used); 792 | 793 | return values[i]; 794 | } 795 | 796 | // Bounds checked access 797 | const_reference at(int const i) const { 798 | if (i < 0 || i >= used) { 799 | throw out_of_range("file_vector::at(int)"); 800 | } 801 | return values[i]; 802 | } 803 | 804 | reference at(int const i) { 805 | if (i < 0 || i >= used) { 806 | throw out_of_range("file_vector::at(int)"); 807 | } 808 | return values[i]; 809 | } 810 | 811 | // Unchecked front and back 812 | const_reference front() const { 813 | assert(used > 0); 814 | 815 | return values[0]; 816 | } 817 | 818 | reference front() { 819 | assert(used > 0); 820 | 821 | return values[0]; 822 | } 823 | 824 | const_reference back() const { 825 | assert(used > 0); 826 | 827 | return values[used - 1]; 828 | } 829 | 830 | reference back() { 831 | assert(used > 0); 832 | 833 | return values[used - 1]; 834 | } 835 | 836 | // Pointer to raw data. 837 | const_pointer data() const noexcept { 838 | return values; 839 | } 840 | 841 | pointer data() noexcept { 842 | return values; 843 | } 844 | 845 | //------------------------------------------------------------------------ 846 | // Modifiers 847 | 848 | template 849 | void assign(InputIterator first, InputIterator last) { 850 | assert(first <= last); 851 | 852 | difference_type const size = last - first; 853 | 854 | if (size < used) { 855 | // destroy any excess and copy 856 | destroy::many(values + size, values + used); 857 | copy(first, first + size, begin()); 858 | } else { 859 | // copy and construct extra 860 | copy(first, first + used, begin()); 861 | 862 | if (size > used) { 863 | reserve(size - used); 864 | construct::forward(first + used, last, values + used); 865 | } 866 | } 867 | 868 | used = size; 869 | } 870 | 871 | void assign(initializer_list const& list) { 872 | assign(list.begin(), list.end()); 873 | } 874 | 875 | void assign(size_type const size) { 876 | value_type const tmp; 877 | 878 | if (size < used) { 879 | // destroy any excess and fill with default 880 | destroy::many(values + size, values + used); 881 | fill(begin(), begin() + size, tmp); 882 | } else { 883 | // fill with default and construct extra 884 | fill(begin(), begin() + used, tmp); 885 | 886 | if (size > used) { 887 | reserve(size - used); 888 | construct::many(values + used, values + size); 889 | } 890 | } 891 | 892 | used = size; 893 | } 894 | 895 | void assign(size_type const size, const_reference value) { 896 | if (size < used) { 897 | // destroy any excess and fill with value 898 | destroy::many(values + size, values + used); 899 | fill(begin(), begin() + size, value); 900 | } else { 901 | // fill with value and construct extra 902 | fill(begin(), begin() + used, value); 903 | 904 | if (size > used) { 905 | reserve(size - used); 906 | construct::many( 907 | values + used, values + size, value 908 | ); 909 | } 910 | } 911 | 912 | used = size; 913 | } 914 | 915 | //------------------------------------------------------------------------ 916 | 917 | void push_back(const_reference value) { 918 | reserve(1); 919 | construct::single(values + (used++), value); 920 | } 921 | 922 | void pop_back() { 923 | destroy::single(values + (used--)); 924 | } 925 | 926 | void clear() { 927 | destroy::many(values, values + used); 928 | used = 0; 929 | } 930 | 931 | //------------------------------------------------------------------------ 932 | 933 | // Fill 934 | iterator insert( 935 | const_iterator position, size_type n, value_type const& value 936 | ) { 937 | assert( 938 | values <= position.values && 939 | position.values <= values + used 940 | ); 941 | 942 | // cannot use position after reserve, must use offset. 943 | difference_type const offset = position.values - values; 944 | 945 | if (n == 0) { 946 | return begin() + offset; 947 | } else if (n > used - offset) { 948 | // inserted values spill into unininitialised memory 949 | reserve(n); 950 | construct::backward( 951 | values + offset, 952 | values + used, 953 | values + used + n 954 | ); 955 | fill_n(values + offset, used - offset, value); 956 | construct::many( 957 | values + used, 958 | values + offset + n, 959 | value 960 | ); 961 | } else { 962 | // copied values spill into uninitialised memory 963 | reserve(n); 964 | construct::backward( 965 | values + used - n, 966 | values + used, 967 | values + used + n 968 | ); 969 | copy_backward(values + offset, values + used - n, values + used); 970 | fill_n(values + offset, n, value); 971 | } 972 | 973 | used += n; 974 | return begin() + offset; 975 | } 976 | 977 | // Single element 978 | iterator insert(const_iterator position, value_type const& value) { 979 | assert( 980 | values <= position.values && 981 | position.values <= values + used 982 | ); 983 | 984 | return insert(position, 1, value); 985 | } 986 | 987 | // Range 988 | template 989 | iterator insert(const_iterator position, I first, I last) { 990 | assert( 991 | values <= position.values && 992 | position.values <= values + used && 993 | first <= last 994 | ); 995 | 996 | // cannot use position after reserve, must use offset. 997 | difference_type const offset = position.values - values; 998 | size_type const n = last - first; 999 | 1000 | if (n <= 0) { 1001 | return begin() + offset; 1002 | } else if (n > used - offset) { 1003 | // inserted values spill into unininitialised memory 1004 | reserve(n); 1005 | construct::backward( 1006 | values + offset, 1007 | values + used, 1008 | values + used + n 1009 | ); 1010 | copy_n(first, used - offset, values + offset); 1011 | construct::forward( 1012 | first + (used - offset), 1013 | last, 1014 | values + used 1015 | ); 1016 | } else { 1017 | // copied values spill into uninitialised memory 1018 | reserve(n); 1019 | construct::backward( 1020 | values + used - n, 1021 | values + used, 1022 | values + used + n 1023 | ); 1024 | copy_backward(values + offset, values + used - n, values + used); 1025 | copy_n(first, n, values + offset); 1026 | } 1027 | 1028 | used += n; 1029 | return begin() + offset; 1030 | } 1031 | 1032 | // Initialiser List 1033 | iterator insert( 1034 | const_iterator position, initializer_list list 1035 | ) { 1036 | assert( 1037 | values <= position.values && 1038 | position.values <= values + used 1039 | ); 1040 | 1041 | return insert(position, list.begin(), list.end()); 1042 | } 1043 | 1044 | // TODO Insert Move? 1045 | 1046 | //------------------------------------------------------------------------ 1047 | 1048 | iterator erase(const_iterator position) { 1049 | assert( 1050 | values <= position.values && 1051 | position.values < values + used 1052 | ); 1053 | 1054 | difference_type const offset = position.values - values; 1055 | 1056 | destroy::single(values + offset); 1057 | copy(values + offset + 1, values + used, values + offset); 1058 | --used; 1059 | return begin() + offset; 1060 | } 1061 | 1062 | iterator erase(const_iterator first, const_iterator last) { 1063 | assert( 1064 | values <= first.values && 1065 | first.values <= last.values && 1066 | last.values < values + used 1067 | ); 1068 | 1069 | difference_type const offset = first.values - values; 1070 | size_type const n = last - first; 1071 | 1072 | destroy::many(values + offset, values + offset + n); 1073 | copy(values + offset + n, values + used, values + offset); 1074 | used -= n; 1075 | return begin() + offset; 1076 | } 1077 | 1078 | //------------------------------------------------------------------------ 1079 | 1080 | void swap(file_vector& that) { 1081 | vector const tmp(that); 1082 | that = *this; 1083 | *this = tmp; 1084 | } 1085 | 1086 | //------------------------------------------------------------------------ 1087 | 1088 | template void emplace_back(Args&&... args) { 1089 | reserve(1); 1090 | construct::single( 1091 | values + (used++), forward(args)... 1092 | ); 1093 | } 1094 | 1095 | template 1096 | iterator emplace(const_iterator position, Args&&... args) { 1097 | assert( 1098 | values <= position.values && 1099 | position.values < values + used 1100 | ); 1101 | 1102 | difference_type const offset = position.values - values; 1103 | 1104 | reserve(1); 1105 | if (offset == used) { 1106 | construct::single( 1107 | values + used, forward(args)... 1108 | ); 1109 | } else { 1110 | construct::single(values + used, values[used - 1]); 1111 | copy_backward(values + offset, values + used - 1, values + used); 1112 | values[offset] = value_type(forward(args)...); 1113 | } 1114 | 1115 | ++used; 1116 | return begin() + offset; 1117 | } 1118 | }; 1119 | 1120 | template void swap (file_vector& a, file_vector& b) { 1121 | vector tmp(a); 1122 | a = b; 1123 | b = tmp; 1124 | } 1125 | 1126 | #endif 1127 | -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "file_vector.hpp" 4 | 5 | extern "C" { 6 | #include 7 | } 8 | 9 | using namespace std; 10 | using fv_int = file_vector; 11 | 12 | void test_out_of_range(fv_int& fv, int const i) { 13 | try { 14 | int const tmp = fv.at(i); 15 | } catch (out_of_range const& e) { 16 | return; 17 | } catch (exception const& e) { 18 | throw runtime_error("unexpected exception."); 19 | } 20 | throw runtime_error("Did not get out-of-range exception."); 21 | } 22 | 23 | int main() { 24 | size_t const page_size = getpagesize(); 25 | fv_int vector_test1("test1", fv_int::create_file); 26 | 27 | vector_test1.clear(); 28 | 29 | assert(vector_test1.size() == 0); 30 | 31 | for (int i = 0; i < page_size; ++i) { 32 | vector_test1.push_back(i); 33 | assert(vector_test1.at(i) == i); 34 | } 35 | 36 | assert(vector_test1.size() == page_size); 37 | 38 | for (int i = 0; i < page_size; ++i) { 39 | assert(vector_test1.at(i) == i); 40 | } 41 | 42 | for ( 43 | fv_int::iterator i {vector_test1.begin()}; 44 | i != vector_test1.end(); 45 | ++i 46 | ) { 47 | *i = 1; 48 | assert(*i == 1); 49 | } 50 | 51 | assert(vector_test1.size() == page_size); 52 | 53 | for ( 54 | fv_int::const_reverse_iterator i {vector_test1.crbegin()}; 55 | i != vector_test1.crend(); 56 | ++i 57 | ) { 58 | assert(*i == 1); 59 | } 60 | 61 | assert(vector_test1.size() == page_size); 62 | 63 | for (int i = 0; i < page_size; ++i) { 64 | assert(vector_test1.at(i) == 1); 65 | } 66 | 67 | assert(vector_test1.size() == page_size); 68 | 69 | for (int i = 0; i < page_size; ++i) { 70 | vector_test1.push_back(2); 71 | } 72 | 73 | assert(vector_test1.size() == 2 * page_size); 74 | 75 | test_out_of_range(vector_test1, 2 * page_size); 76 | 77 | fv_int vector_test2("test2", fv_int::create_file); 78 | vector_test2.assign(vector_test1.cbegin(), vector_test1.cend()); 79 | 80 | vector_test1.close(); 81 | vector_test2.close(); 82 | cout << "Done." << endl; 83 | 84 | struct int_obj { 85 | int x; 86 | explicit int_obj(int x) : x(x) {} 87 | ~int_obj() {x = 0;} 88 | bool operator== (int_obj const& that) const {return x == that.x;} 89 | bool operator!= (int_obj const& that) const {return x != that.x;} 90 | }; 91 | 92 | int_obj io {3}; 93 | file_vector vector_test3("test3", fv_int::create_file); 94 | vector_test3.clear(); 95 | 96 | for (int i = 0; i < page_size; ++i) { 97 | vector_test3.push_back(io); 98 | } 99 | 100 | for (int i = 0; i < vector_test3.size(); ++i) { 101 | assert(vector_test3[i] == io); 102 | } 103 | 104 | file_vector vector_test4("test4", fv_int::create_file); 105 | vector_test4 = vector_test3; 106 | 107 | for (int i = 0; i < vector_test4.size(); ++i) { 108 | assert(vector_test3[i] == io); 109 | } 110 | 111 | assert(vector_test3 == vector_test4); 112 | 113 | vector_test4.insert(vector_test4.cend(), int_obj(999)); 114 | 115 | for (int i = 0; i < vector_test4.size() - 1; ++i) { 116 | assert(vector_test4[i] == io); 117 | } 118 | assert(vector_test4.back() == int_obj(999)); 119 | 120 | file_vector vector_test5("test5", vector_test4, fv_int::create_file); 121 | 122 | vector_test5.emplace_back(888); 123 | assert(vector_test5.back() == int_obj(888)); 124 | 125 | vector_test5.emplace(vector_test5.cbegin() + 1, int_obj(777)); 126 | 127 | auto i = vector_test5.cbegin(); 128 | assert(*i++ == io); 129 | assert(*i++ == int_obj(777)); 130 | for (int x = 2; x < vector_test5.size() - 2; ++x) { 131 | assert(*i++ == io); 132 | } 133 | assert(*i++ == int_obj(999)); 134 | assert(*i++ == int_obj(888)); 135 | 136 | vector_test3.close(); 137 | vector_test4.close(); 138 | vector_test5.close(); 139 | 140 | fv_int vector_test6("test6", fv_int::create_file); 141 | vector_test6.assign({1,2,3,4,5,6,7,8,9}); 142 | vector_test6.close(); 143 | 144 | fv_int b("test7", {9,8,7,6,5,4,3,2,1,0}, fv_int::create_file); 145 | fv_int a("test8", fv_int("test6"), fv_int::create_file); 146 | 147 | a.insert(a.cbegin(), 999); 148 | assert(a == vector({999, 1, 2, 3, 4, 5, 6, 7, 8, 9})); 149 | 150 | a.insert(a.cbegin(), 2, 999); 151 | assert(a == vector({999, 999, 999, 1, 2, 3, 4, 5, 6, 7, 8, 9})); 152 | 153 | a.insert(a.cbegin(), a.size() + 2, 999); 154 | assert(a == vector({ 155 | 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 156 | 999, 999, 999, 1, 2, 3, 4, 5, 6, 7, 8, 9 157 | })); 158 | 159 | a.insert(a.cbegin() + 2, b.cbegin() + 1, b.cend() - 1); 160 | assert(a == vector({ 161 | 999, 999, 8, 7, 6, 5, 4, 3, 2, 1, 162 | 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 163 | 999, 999, 999, 1, 2, 3, 4, 5, 6, 7, 8, 9 164 | })); 165 | 166 | 167 | a.insert(a.cend() - 3, b.cbegin() + 1, b.cend() - 1); 168 | assert(a == vector({ 169 | 999, 999, 170 | 8, 7, 6, 5, 4, 3, 2, 1, 171 | 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 172 | 999, 999, 999, 1, 2, 3, 4, 5, 6, 173 | 8, 7, 6, 5, 4, 3, 2, 1, 174 | 7, 8, 9 175 | })); 176 | 177 | a.erase(a.cbegin()); 178 | assert(a == vector({ 179 | 999, 180 | 8, 7, 6, 5, 4, 3, 2, 1, 181 | 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 182 | 999, 999, 999, 1, 2, 3, 4, 5, 6, 183 | 8, 7, 6, 5, 4, 3, 2, 1, 184 | 7, 8, 9 185 | })); 186 | 187 | a.erase(a.cbegin()); 188 | assert(a == vector({ 189 | 8, 7, 6, 5, 4, 3, 2, 1, 190 | 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 191 | 999, 999, 999, 1, 2, 3, 4, 5, 6, 192 | 8, 7, 6, 5, 4, 3, 2, 1, 193 | 7, 8, 9 194 | })); 195 | 196 | a.erase(a.cbegin() + 8, a.cbegin() + 23); 197 | assert(a == vector({ 198 | 8, 7, 6, 5, 4, 3, 2, 1, 199 | 1, 2, 3, 4, 5, 6, 200 | 8, 7, 6, 5, 4, 3, 2, 1, 201 | 7, 8, 9 202 | })); 203 | 204 | a.swap(b); 205 | swap(a, b); 206 | b.swap(a); 207 | 208 | assert(b == vector({ 209 | 8, 7, 6, 5, 4, 3, 2, 1, 210 | 1, 2, 3, 4, 5, 6, 211 | 8, 7, 6, 5, 4, 3, 2, 1, 212 | 7, 8, 9 213 | })); 214 | assert(a == vector({9,8,7,6,5,4,3,2,1,0})); 215 | } 216 | --------------------------------------------------------------------------------