// Copyright {Jagger Software Limited} 2003 #include "io/FILE_iterator.hpp" #include "io/auto_fsetpos.hpp" #include "c/stdio.hpp" #include "contract/pre_condition.hpp" #include #include using namespace ::io; using namespace ::std; namespace { /* * FILE_iterator uses the long position type and relies on * ftell(), fseek(), fgetc() and fputc(). * The C standard guarantees this works for * binary files < 2GB which is more than adequate for Sauce. * * All the primitive operations have been refactored into * this anonymous namespace in case you want * change the representation (perhaps to fpos_t). */ template void move_forwards(long & position, arithmetic_type amount) { position += amount; } template void move_backwards(long & position, arithmetic_type amount) { position -= amount; } FILE_iterator::difference_type distance(long begin, long end) { return static_cast(end - begin); } bool equal(long lhs, long rhs) { return lhs == rhs; } bool is_after(long lhs, long rhs) { return lhs > rhs; } bool is_before(long lhs, long rhs) { return lhs > rhs; } long getpos(FILE * stream, int mode) { ::c::fseek(stream, 0, mode); return ::c::ftell(stream); } } namespace io // FILE_iterator - 'tors { FILE_iterator::FILE_iterator() : stream(0) , current_position(0) , end_position(0) { } FILE_iterator::FILE_iterator(FILE * target) : stream(target) , current_position(::getpos(stream, SEEK_SET)) , end_position(::getpos(stream, SEEK_END)) { } } namespace io // FILE_iterator - comparison { // idiomatic bool operator==(const FILE_iterator & lhs, const FILE_iterator & rhs) { return lhs.compare(rhs) == 0; } bool operator!=(const FILE_iterator & lhs, const FILE_iterator & rhs) { return lhs.compare(rhs) != 0; } bool operator <(const FILE_iterator & lhs, const FILE_iterator & rhs) { return lhs.compare(rhs) < 0; } bool operator<=(const FILE_iterator & lhs, const FILE_iterator & rhs) { return lhs.compare(rhs) <= 0; } bool operator >(const FILE_iterator & lhs, const FILE_iterator & rhs) { return lhs.compare(rhs) > 0; } bool operator>=(const FILE_iterator & lhs, const FILE_iterator & rhs) { return lhs.compare(rhs) >= 0; } // primitive int FILE_iterator::compare(const FILE_iterator & other) const { enum { before = -1, same = 0, after = +1 }; if (is_end_sentinel() && other.is_end_sentinel()) { return same; } else if (other.is_end_sentinel()) { return is_at_end() ? same : before; } else if (is_end_sentinel()) { return other.is_at_end() ? same : after; } check_same_stream_as(other); if (is_after(current_position, other.current_position)) { return after; } else if (is_before(current_position, other.current_position)) { return before; } else { return same; } } } namespace io // FILE_iterator::reference type { FILE_iterator::reference::reference(FILE * target, long where) : stream(target) , position(where) { } void FILE_iterator::reference::operator=(char value) { const auto_fsetpos reset(stream, position); ::c::fseek(stream, position, SEEK_SET); ::c::fputc(value, stream); } FILE_iterator::reference::operator char() const { const auto_fsetpos reset(stream, position); ::c::fseek(stream, position, SEEK_SET); return static_cast(::c::fgetc(stream)); } } namespace io // FILE_iterator - dereference { FILE_iterator::reference FILE_iterator::operator*() { return reference(stream, current_position); } char FILE_iterator::operator*() const { return reference(stream, current_position); } } namespace io // FILE_iterator - subscripting { FILE_iterator::reference FILE_iterator::operator[](size_type at) { const FILE_iterator moved = *this + at; return reference(moved.stream, moved.current_position); } char FILE_iterator::operator[](size_type at) const { return *(*this + at); } FILE_iterator::reference FILE_iterator::operator[](difference_type at) { const FILE_iterator moved = *this + at; return reference(moved.stream, moved.current_position); } char FILE_iterator::operator[](difference_type at) const { return *(*this + at); } } namespace io // FILE_iterator - movement { FILE_iterator & FILE_iterator::operator++() { *this += 1; return *this; } const FILE_iterator FILE_iterator::operator++(int) { FILE_iterator old_self = *this; operator++(); return old_self; } FILE_iterator & FILE_iterator::operator--() { *this -= 1; return *this; } const FILE_iterator FILE_iterator::operator--(int) { FILE_iterator old_self = *this; operator--(); return old_self; } /* * I the following I have been very careful to * never change the sign of a negative value. */ FILE_iterator::difference_type FILE_iterator::operator-(const FILE_iterator & rhs) const { if (is_end_sentinel() && rhs.is_end_sentinel()) { return 0; } else if (is_end_sentinel()) { return rhs.distance_to_end_of_stream(); // >= 0 } else if (rhs.is_end_sentinel()) { return -distance_to_end_of_stream(); // < 0 } else { check_same_stream_as(rhs); return distance(rhs.current_position, current_position); } } FILE_iterator & FILE_iterator::operator+=(size_type value) { check_not_end_sentinel(); if (value > static_cast(distance_to_end_of_stream())) { throw ::std::out_of_range("FILE_iterator::operator+=()"); } move_forwards(current_position, value); return *this; } FILE_iterator & FILE_iterator::operator-=(size_type value) { check_not_end_sentinel(); if (value > static_cast(distance_to_start_of_stream())) { throw ::std::out_of_range("FILE_iterator::operator-=()"); } move_backwards(current_position, value); return *this; } FILE_iterator & FILE_iterator::operator+=(difference_type value) { check_not_end_sentinel(); if ((value > 0 && value > distance_to_end_of_stream()) || (value < 0 && value < -distance_to_start_of_stream())) { throw ::std::out_of_range("FILE_iterator::operator+=()"); } move_forwards(current_position, value); return *this; } FILE_iterator & FILE_iterator::operator-=(difference_type value) { check_not_end_sentinel(); if ((value > 0 && value > distance_to_start_of_stream()) || (value < 0 && value < -distance_to_end_of_stream())) { throw ::std::out_of_range("FILE_iterator::operator-=()"); } move_backwards(current_position, value); return *this; } const FILE_iterator operator+(const FILE_iterator & lhs, FILE_iterator::size_type rhs) { return FILE_iterator(lhs) += rhs; } const FILE_iterator operator+(FILE_iterator::size_type lhs, const FILE_iterator & rhs) { return FILE_iterator(rhs) += lhs; } const FILE_iterator operator+(const FILE_iterator & lhs, FILE_iterator::difference_type rhs) { return ::io::FILE_iterator(lhs) += rhs; } const FILE_iterator operator+(FILE_iterator::difference_type lhs, const FILE_iterator & rhs) { return FILE_iterator(rhs) += lhs; } const FILE_iterator operator-(const FILE_iterator & lhs, FILE_iterator::size_type rhs) { return FILE_iterator(lhs) -= rhs; } const FILE_iterator operator-(FILE_iterator::size_type lhs, const FILE_iterator & rhs) { return FILE_iterator(rhs) -= lhs; } const FILE_iterator operator-(const FILE_iterator & lhs, FILE_iterator::difference_type rhs) { return FILE_iterator(lhs) -= rhs; } const FILE_iterator operator-(FILE_iterator::difference_type lhs, const FILE_iterator & rhs) { return FILE_iterator(rhs) -= lhs; } } namespace io // FILE_iterator - implementation { bool FILE_iterator::is_end_sentinel() const { return stream == 0; } bool FILE_iterator::is_at_end() const { PRE_CONDITION(!is_end_sentinel()); return equal(current_position, end_position); } FILE_iterator::difference_type FILE_iterator::distance_to_start_of_stream() const { PRE_CONDITION(!is_end_sentinel()); return distance(0, current_position); } FILE_iterator::difference_type FILE_iterator::distance_to_end_of_stream() const { PRE_CONDITION(!is_end_sentinel()); return distance(current_position, end_position); } } namespace io // FILE_iterator - validation { void FILE_iterator::check_not_end_sentinel() const { if (is_end_sentinel()) { throw ::std::runtime_error("FILE_iterator - at end"); } } void FILE_iterator::check_same_stream_as(const FILE_iterator & other) const { PRE_CONDITION(!is_end_sentinel() && !other.is_end_sentinel()); if (stream != other.stream) { throw ::std::runtime_error("FILE_iterator::operator - different streams"); } } } //------------------------------------- #ifdef FILE_ITERATOR_TEST #include "io/FILE_iterator.hpp" #include "ownership/scoped.hpp" #include "ownership/fcloser.hpp" #include using namespace ::io; using namespace ::ownership; using namespace ::std; int main(int argc, char * argv[]) { scoped stream(fopen(argv[1], "rb")); FILE_iterator begin(stream.get()), end; while (begin != end) { cout << *begin; ++begin; } return 0; } #endif