1 // Copyright (c) 2016 The WebM project authors. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 #include "webm/istream_reader.h"
9
10 #include <cassert>
11 #include <cstdint>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <limits>
15 #include <memory>
16
17 #include "webm/status.h"
18
19 namespace webm {
20
IstreamReader(IstreamReader && other)21 IstreamReader::IstreamReader(IstreamReader&& other)
22 : istream_(std::move(other.istream_)), position_(other.position_) {
23 other.position_ = 0;
24 }
25
operator =(IstreamReader && other)26 IstreamReader& IstreamReader::operator=(IstreamReader&& other) {
27 if (this != &other) {
28 istream_ = std::move(other.istream_);
29 position_ = other.position_;
30 other.position_ = 0;
31 }
32 return *this;
33 }
34
Read(std::size_t num_to_read,std::uint8_t * buffer,std::uint64_t * num_actually_read)35 Status IstreamReader::Read(std::size_t num_to_read, std::uint8_t* buffer,
36 std::uint64_t* num_actually_read) {
37 assert(num_to_read > 0);
38 assert(buffer != nullptr);
39 assert(num_actually_read != nullptr);
40
41 if (istream_ == nullptr) {
42 *num_actually_read = 0;
43 return Status(Status::kEndOfFile);
44 }
45
46 using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
47 constexpr std::streamsize streamsize_max =
48 std::numeric_limits<std::streamsize>::max();
49 std::streamsize limited_num_to_read;
50 if (num_to_read > static_cast<unsigned_streamsize>(streamsize_max)) {
51 limited_num_to_read = streamsize_max;
52 } else {
53 limited_num_to_read = static_cast<std::streamsize>(num_to_read);
54 }
55
56 istream_->read(reinterpret_cast<char*>(buffer), limited_num_to_read);
57 std::streamsize actual = istream_->gcount();
58 *num_actually_read = static_cast<std::uint64_t>(actual);
59 position_ += *num_actually_read;
60
61 if (actual == 0) {
62 return Status(Status::kEndOfFile);
63 }
64
65 if (static_cast<std::size_t>(actual) == num_to_read) {
66 return Status(Status::kOkCompleted);
67 } else {
68 return Status(Status::kOkPartial);
69 }
70 }
71
Skip(std::uint64_t num_to_skip,std::uint64_t * num_actually_skipped)72 Status IstreamReader::Skip(std::uint64_t num_to_skip,
73 std::uint64_t* num_actually_skipped) {
74 assert(num_to_skip > 0);
75 assert(num_actually_skipped != nullptr);
76
77 *num_actually_skipped = 0;
78 if (istream_ == nullptr || !istream_->good()) {
79 return Status(Status::kEndOfFile);
80 }
81
82 // Try seeking forward first.
83 using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
84 constexpr std::streamsize streamsize_max =
85 std::numeric_limits<std::streamsize>::max();
86 std::streamsize seek_offset;
87 if (num_to_skip > static_cast<unsigned_streamsize>(streamsize_max)) {
88 seek_offset = streamsize_max;
89 } else {
90 seek_offset = static_cast<std::streamsize>(num_to_skip);
91 }
92 if (istream_->seekg(seek_offset, std::ios_base::cur)) {
93 *num_actually_skipped = static_cast<std::uint64_t>(seek_offset);
94 position_ += static_cast<std::uint64_t>(seek_offset);
95 if (static_cast<std::uint64_t>(seek_offset) == num_to_skip) {
96 return Status(Status::kOkCompleted);
97 } else {
98 return Status(Status::kOkPartial);
99 }
100 }
101 istream_->clear();
102
103 // Seeking doesn't work on things like pipes, so if seeking failed then fall
104 // back to reading the data into a junk buffer.
105 std::size_t actual = 0;
106 do {
107 char junk[1024];
108 std::streamsize num_to_read = static_cast<std::streamsize>(sizeof(junk));
109 if (num_to_skip < static_cast<std::uint64_t>(num_to_read)) {
110 num_to_read = static_cast<std::streamsize>(num_to_skip);
111 }
112
113 istream_->read(junk, num_to_read);
114 std::streamsize actual = istream_->gcount();
115 *num_actually_skipped += static_cast<std::uint64_t>(actual);
116 position_ += static_cast<std::uint64_t>(actual);
117 num_to_skip -= static_cast<std::uint64_t>(actual);
118 } while (actual > 0 && num_to_skip > 0);
119
120 if (*num_actually_skipped == 0) {
121 return Status(Status::kEndOfFile);
122 }
123
124 if (num_to_skip == 0) {
125 return Status(Status::kOkCompleted);
126 } else {
127 return Status(Status::kOkPartial);
128 }
129 }
130
Position() const131 std::uint64_t IstreamReader::Position() const { return position_; }
132
133 } // namespace webm
134