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