1 //
2 // reversed_memory_stream.cpp
3 //
4 // Copyright © 2024 Apple Inc. All rights reserved.
5 //
6 // Please refer to the license found in the LICENSE file in the root directory of the source tree.
7
8 #include "reversed_memory_stream.hpp"
9
10 #include <limits>
11
12 namespace inmemoryfs {
13
ReversedIMemoryStreamBuf(std::shared_ptr<MemoryBuffer> buffer)14 ReversedIMemoryStreamBuf::ReversedIMemoryStreamBuf(std::shared_ptr<MemoryBuffer> buffer) noexcept
15 : buffer_(buffer), start_(static_cast<char*>(buffer->data())), current_(start_), end_(start_ + buffer->size()) {
16 // we are intentionally setting `gptr` to the buffer end, this makes sure
17 // that `underflow` and `uflow` methods are always called.
18 setg(start_, start_, start_);
19 }
20
iseekoff(std::streambuf::off_type offset,std::ios_base::seekdir dir)21 std::streambuf::pos_type ReversedIMemoryStreamBuf::iseekoff(std::streambuf::off_type offset,
22 std::ios_base::seekdir dir) {
23 std::streambuf::off_type next = std::streambuf::off_type(-1);
24 const std::ptrdiff_t size = std::ptrdiff_t(buffer_->size());
25 switch (dir) {
26 case std::ios_base::beg: {
27 next = offset;
28 break;
29 }
30 case std::ios_base::cur: {
31 next = std::streambuf::off_type(std::ptrdiff_t(current_ - start_)) + offset;
32 break;
33 }
34 case std::ios_base::end: {
35 next = std::streambuf::off_type(size) + offset;
36 break;
37 }
38 default:
39 break;
40 }
41 if (next < 0 || next >= std::streambuf::off_type(size)) {
42 return std::streambuf::pos_type(std::streambuf::off_type(-1));
43 }
44 current_ = start_ + offset;
45 setg(start_, current_, current_);
46 return std::streambuf::pos_type(current_ - start_);
47 }
48
seekpos(std::streambuf::pos_type pos,std::ios_base::openmode which)49 std::streambuf::pos_type ReversedIMemoryStreamBuf::seekpos(std::streambuf::pos_type pos,
50 std::ios_base::openmode which) {
51 if ((which & std::ios_base::in) == 0) {
52 return std::streambuf::pos_type(std::streambuf::off_type(-1));
53 }
54 return iseekoff(pos - std::streambuf::pos_type(std::streambuf::off_type(0)), std::ios::beg);
55 }
56
showmanyc()57 std::streamsize ReversedIMemoryStreamBuf::showmanyc() {
58 std::ptrdiff_t n = end_ - current_;
59 std::streamsize max = std::numeric_limits<std::streamsize>::max();
60 return (n <= max ? std::streamsize(n) : max);
61 }
62
read(char * pos)63 std::streambuf::int_type ReversedIMemoryStreamBuf::read(char* pos) {
64 std::ptrdiff_t offset = pos - start_;
65 // offset from the end
66 std::ptrdiff_t offsetFromEnd = buffer_->size() - offset - 1;
67 return traits_type::to_int_type(start_[offsetFromEnd]);
68 }
69
pbackfail(std::streambuf::int_type ch)70 std::streambuf::int_type ReversedIMemoryStreamBuf::pbackfail(std::streambuf::int_type ch) {
71 if (current_ == end_ || (ch != traits_type::eof() && ch != read(current_ - 1))) {
72 return traits_type::eof();
73 }
74 --current_;
75 setg(start_, current_, current_);
76 return ch;
77 }
78
underflow()79 std::streambuf::int_type ReversedIMemoryStreamBuf::underflow() {
80 if (current_ == end_) {
81 return traits_type::eof();
82 }
83 return traits_type::to_int_type(read(current_));
84 }
85
uflow()86 std::streambuf::int_type ReversedIMemoryStreamBuf::uflow() {
87 if (current_ == end_) {
88 return traits_type::eof();
89 }
90 auto ch = read(current_);
91 ++current_;
92 setg(start_, current_, current_);
93 return ch;
94 }
95
xsgetn(char * s,std::streamsize n)96 std::streamsize ReversedIMemoryStreamBuf::xsgetn(char* s, std::streamsize n) {
97 const ptrdiff_t remaining = (static_cast<char*>(buffer_->data()) + buffer_->size()) - current_;
98 n = std::min(n, remaining);
99 if (n <= 0) {
100 return std::streamsize(0);
101 }
102 std::ptrdiff_t offset = current_ - start_;
103 std::ptrdiff_t pos = buffer_->size() - 1 - offset;
104 for (std::streamsize i = 0; i < n; i++) {
105 s[i] = start_[pos - i];
106 }
107 current_ += n;
108 setg(start_, current_, current_);
109 return n;
110 }
111
ReversedIMemoryStream(const std::shared_ptr<MemoryBuffer> & buffer)112 ReversedIMemoryStream::ReversedIMemoryStream(const std::shared_ptr<MemoryBuffer>& buffer) noexcept
113 : std::istream(nullptr), streambuf(buffer) {
114 rdbuf(&streambuf);
115 }
116 } // namespace inmemoryfs
117