Skip to content

Commit

Permalink
adds ranges::basic_unformatted_istream_view
Browse files Browse the repository at this point in the history
`istream_view` is only useful if the input to be formatted on read
and `std::istreambuf_iterator` only works with types that have a
`std::char_traits` specialisation.

The purpose of `unformatted_istream_view` is to read from an istream
object without formatting (so integers will be read as binary rather
than as text).
  • Loading branch information
cjdb committed Dec 29, 2020
1 parent d098b96 commit ac78edc
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,6 @@ $RECYCLE.BIN/
\#*#
.#*
/CMakeSettings.json

# clangd cache
.cache/clangd/
1 change: 1 addition & 0 deletions include/range/v3/view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
#include <range/v3/view/transform.hpp>
#include <range/v3/view/trim.hpp>
#include <range/v3/view/unbounded.hpp>
#include <range/v3/view/unformatted_istream.hpp>
#include <range/v3/view/unique.hpp>
#include <range/v3/view/view.hpp>
#include <range/v3/view/zip.hpp>
Expand Down
119 changes: 119 additions & 0 deletions include/range/v3/view/unformatted_istream.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/// \file
// Range v3 library
//
// Copyright Eric Niebler 2013-present
// Copyright Google LLC 2020-present
//
// Use, modification and distribution is subject to the
// Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3
//

#ifndef RANGES_V3_VIEW_UNFORMATTED_ISTREAM_HPP
#define RANGES_V3_VIEW_UNFORMATTED_ISTREAM_HPP

#include <istream>

#include <range/v3/range_fwd.hpp>

#include <range/v3/iterator/default_sentinel.hpp>
#include <range/v3/utility/semiregular_box.hpp>
#include <range/v3/utility/static_const.hpp>
#include <range/v3/view/facade.hpp>

#include <range/v3/detail/prologue.hpp>

namespace ranges
{
/// \addtogroup group-views
/// @{
template<typename Val, typename CharT = char, typename Traits = std::char_traits<CharT>>
struct basic_unformatted_istream_view
: view_facade<basic_unformatted_istream_view<Val, CharT, Traits>, unknown>
{
private:
friend range_access;
std::basic_istream<CharT, Traits> * sin_;
semiregular_box_t<Val> obj_;
struct cursor
{
private:
friend range_access;
using single_pass = std::true_type;
basic_unformatted_istream_view * rng_ = nullptr;

public:
cursor() = default;
explicit cursor(basic_unformatted_istream_view * rng) noexcept
: rng_(rng)
{}
void next()
{
rng_->next();
}
Val & read() const noexcept
{
return rng_->cached();
}
bool equal(default_sentinel_t) const noexcept
{
return !rng_->sin_;
}
bool equal(cursor that) const noexcept
{
return !rng_->sin_ == !that.rng_->sin_;
}
};
void next()
{
if(!sin_->read(reinterpret_cast<CharT*>(std::addressof(cached())), sizeof(Val))) {
sin_ = nullptr;
}
}
cursor begin_cursor()
{
return cursor{this};
}

public:
basic_unformatted_istream_view() = default;
explicit basic_unformatted_istream_view(std::istream & sin)
noexcept(std::is_nothrow_default_constructible<Val>::value)
: sin_(&sin)
, obj_{}
{
// The current `value_` is considered stale, so we'd better update it (otherwise the
// first value in the range will *always* be `Val()`.)
next();
}
Val & cached() noexcept
{
return obj_;
}
};

/// \cond
namespace _unformatted_istream_
{
/// \endcond
template(typename Val, typename CharT, typename Traits)(
/// \pre
requires copy_constructible<Val> AND default_constructible<Val>)
inline basic_unformatted_istream_view<Val, CharT, Traits>
unformatted_istream_view(std::basic_istream<CharT, Traits> & sin)
{
return basic_unformatted_istream_view<Val>{sin};
}
/// \cond
} // namespace _unformatted_istream_
using namespace _unformatted_istream_;
/// \endcond
/// @}
} // namespace ranges

#include <range/v3/detail/epilogue.hpp>

#endif
1 change: 1 addition & 0 deletions test/view/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ rv3_add_test(test.view.take_while view.take_while take_while.cpp)
rv3_add_test(test.view.tokenize view.tokenize tokenize.cpp)
rv3_add_test(test.view.transform view.transform transform.cpp)
rv3_add_test(test.view.trim view.trim trim.cpp)
rv3_add_test(test.view.unformatted_istream view.unformatted_istream unformatted_istream.cpp)
rv3_add_test(test.view.unique view.unique unique.cpp)
rv3_add_test(test.view.view view.view view.cpp)
rv3_add_test(test.view.zip view.zip zip.cpp)
Expand Down
88 changes: 88 additions & 0 deletions test/view/unformatted_istream.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Range v3 library
//
// Copyright Eric Niebler 2014-present
// Copyright Google LLC 2020-present
//
// Use, modification and distribution is subject to the
// Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// Project home: https://github.com/ericniebler/range-v3
#include <range/v3/view/unformatted_istream.hpp>

#include <range/v3/algorithm/copy.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/for_each.hpp>
#include <range/v3/view/reverse.hpp>
#include <range/v3/view/sliding.hpp>
#include <range/v3/view/stride.hpp>
#include <string>
#include <sstream>
#include <vector>

#include <range/v3/iterator.hpp>

#include "../simple_test.hpp"
#include "../test_utils.hpp"

namespace rv = ranges::views;

namespace std {
template<typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> const& v)
{
os << std::hex;
ranges::copy(v, ::ranges::ostream_iterator<>(os, " "));
return os;
}
}

int main()
{
{
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
auto const input = std::vector<std::string>(3, "\x21\x22\x23\x24");
#else
auto const input = std::vector<std::string>{
"\x21\x22\x23\x24",
"\x22\x21\x24\x23",
"\x24\x23\x22\x21",
};
#endif // endianness
{
auto in = std::istringstream(input[0]);
auto const expected = std::vector<std::uint8_t>{0x21, 0x22, 0x23, 0x24};
auto const actual = ranges::unformatted_istream_view<std::uint8_t>(in) | ranges::to<std::vector>;
CHECK(actual == expected);
}

{
auto in = std::istringstream(input[1]);
auto const expected = std::vector<std::uint16_t>{0x2122, 0x2324};
auto const actual = ranges::unformatted_istream_view<std::uint16_t>(in) | ranges::to<std::vector>;
CHECK(actual == expected);
}
{
auto in = std::istringstream(input[2]);
auto const expected = std::vector<std::uint32_t>{0x21222324};
auto const actual = ranges::unformatted_istream_view<std::uint32_t>(in) | ranges::to<std::vector>;
CHECK(actual == expected);
}
{
auto in = std::istringstream(input[2]);
auto const actual = ranges::unformatted_istream_view<std::uint64_t>(in) | ranges::to<std::vector>;
CHECK(in.fail());
}
#if __cplusplus >= 201703L
{
auto in = std::istringstream(input[2]);
auto const expected = std::vector<float>{0x1.4446480000000000179Dp-61f};
auto const actual = ranges::unformatted_istream_view<float>(in) | ranges::to<std::vector>;
CHECK(actual == expected);
}
#endif // __cplusplus >= 201703L
}

return ::test_result();
}

0 comments on commit ac78edc

Please sign in to comment.