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/file_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 
FileReader(FILE * file)21 FileReader::FileReader(FILE* file) : file_(file) { assert(file); }
22 
FileReader(FileReader && other)23 FileReader::FileReader(FileReader&& other)
24     : file_(std::move(other.file_)), position_(other.position_) {
25   other.position_ = 0;
26 }
27 
operator =(FileReader && other)28 FileReader& FileReader::operator=(FileReader&& other) {
29   if (this != &other) {
30     file_ = std::move(other.file_);
31     position_ = other.position_;
32     other.position_ = 0;
33   }
34   return *this;
35 }
36 
Read(std::size_t num_to_read,std::uint8_t * buffer,std::uint64_t * num_actually_read)37 Status FileReader::Read(std::size_t num_to_read, std::uint8_t* buffer,
38                         std::uint64_t* num_actually_read) {
39   assert(num_to_read > 0);
40   assert(buffer != nullptr);
41   assert(num_actually_read != nullptr);
42 
43   if (file_ == nullptr) {
44     *num_actually_read = 0;
45     return Status(Status::kEndOfFile);
46   }
47 
48   std::size_t actual =
49       std::fread(static_cast<void*>(buffer), 1, num_to_read, file_.get());
50   *num_actually_read = static_cast<std::uint64_t>(actual);
51   position_ += *num_actually_read;
52 
53   if (actual == 0) {
54     return Status(Status::kEndOfFile);
55   }
56 
57   if (actual == num_to_read) {
58     return Status(Status::kOkCompleted);
59   } else {
60     return Status(Status::kOkPartial);
61   }
62 }
63 
Skip(std::uint64_t num_to_skip,std::uint64_t * num_actually_skipped)64 Status FileReader::Skip(std::uint64_t num_to_skip,
65                         std::uint64_t* num_actually_skipped) {
66   assert(num_to_skip > 0);
67   assert(num_actually_skipped != nullptr);
68 
69   *num_actually_skipped = 0;
70 
71   if (file_ == nullptr) {
72     return Status(Status::kEndOfFile);
73   }
74 
75   // Try seeking forward first.
76   long seek_offset = std::numeric_limits<long>::max();  // NOLINT
77   if (num_to_skip < static_cast<unsigned long>(seek_offset)) {  // NOLINT
78     seek_offset = static_cast<long>(num_to_skip);  // NOLINT
79   }
80   // TODO(mjbshaw): Use fseeko64/_fseeki64 if available.
81   if (!std::fseek(file_.get(), seek_offset, SEEK_CUR)) {
82     *num_actually_skipped = static_cast<std::uint64_t>(seek_offset);
83     position_ += static_cast<std::uint64_t>(seek_offset);
84     if (static_cast<unsigned long>(seek_offset) == num_to_skip) {  // NOLINT
85       return Status(Status::kOkCompleted);
86     } else {
87       return Status(Status::kOkPartial);
88     }
89   }
90   std::clearerr(file_.get());
91 
92   // Seeking doesn't work on things like pipes, so if seeking failed then fall
93   // back to reading the data into a junk buffer.
94   std::size_t actual = 0;
95   do {
96     std::uint8_t junk[1024];
97     std::size_t num_to_read = sizeof(junk);
98     if (num_to_skip < num_to_read) {
99       num_to_read = static_cast<std::size_t>(num_to_skip);
100     }
101 
102     std::size_t actual =
103         std::fread(static_cast<void*>(junk), 1, num_to_read, file_.get());
104     *num_actually_skipped += static_cast<std::uint64_t>(actual);
105     position_ += static_cast<std::uint64_t>(actual);
106     num_to_skip -= static_cast<std::uint64_t>(actual);
107   } while (actual > 0 && num_to_skip > 0);
108 
109   if (*num_actually_skipped == 0) {
110     return Status(Status::kEndOfFile);
111   }
112 
113   if (num_to_skip == 0) {
114     return Status(Status::kOkCompleted);
115   } else {
116     return Status(Status::kOkPartial);
117   }
118 }
119 
Position() const120 std::uint64_t FileReader::Position() const { return position_; }
121 
122 }  // namespace webm
123