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