aboutsummaryrefslogtreecommitdiff
path: root/src/common/io/UnixFileStream.cpp
blob: 9c9abba5cc97c97e6eb0f3f1c70454bc1393afc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "cru/common/io/UnixFileStream.hpp"
#include "cru/common/ErrnoException.hpp"
#include "cru/common/io/OpenFileFlag.hpp"

#include <fcntl.h>
#include <sys/_types/_s_ifmt.h>
#include <unistd.h>

namespace cru::io {

namespace {
int MapOpenFileFlag(OpenFileFlag flags) {
  int result = 0;
  if (flags & OpenFileFlags::Read) {
    if (flags & OpenFileFlags::Write) {
      result |= O_RDWR;
    } else {
      result |= O_RDONLY;
    }
  } else {
    if (flags & OpenFileFlags::Write) {
      result |= O_WRONLY;
    } else {
      throw Exception(u"Invalid open file flag.");
    }
  }

  if (flags & OpenFileFlags::Append) {
    result |= O_APPEND;
  }

  if (flags & OpenFileFlags::Create) {
    result |= O_CREAT;
  }

  if (flags & OpenFileFlags::ThrowOnExist) {
    result |= O_EXCL;
  }

  return result;
}

int MapSeekOrigin(Stream::SeekOrigin origin) {
  switch (origin) {
    case Stream::SeekOrigin::Begin:
      return SEEK_SET;
    case Stream::SeekOrigin::Current:
      return SEEK_CUR;
    case Stream::SeekOrigin::End:
      return SEEK_END;
    default:
      throw Exception(u"Invalid seek origin.");
  }
}
}  // namespace

UnixFileStream::~UnixFileStream() { Close(); }

UnixFileStream::UnixFileStream(String path, OpenFileFlag flags)
    : path_(std::move(path)), flags_(flags) {
  auto p = path_.ToUtf8();
  file_descriptor_ =
      ::open(p.c_str(), MapOpenFileFlag(flags_), S_IRUSR | S_IWUSR);
  if (file_descriptor_ == -1) {
    throw ErrnoException(
        Format(u"Failed to open file {} with flags {}.", path_, flags_.value));
  }
}

bool UnixFileStream::CanSeek() {
  CheckClosed();
  return true;
}

Index UnixFileStream::Tell() {
  CheckClosed();
  auto result = ::lseek(file_descriptor_, 0, SEEK_CUR);
  if (result == -1) {
    throw ErrnoException(u"Failed to get file position.");
  }
  return result;
}

void UnixFileStream::Seek(Index offset, SeekOrigin origin) {
  CheckClosed();
  int result = ::lseek(file_descriptor_, offset, MapSeekOrigin(origin));
  if (result == -1) {
    throw ErrnoException(u"Failed to seek file.");
  }
}

bool UnixFileStream::CanRead() {
  CheckClosed();
  return flags_ & OpenFileFlags::Read;
}

Index UnixFileStream::Read(std::byte *buffer, Index offset, Index size) {
  CheckClosed();
  auto result = ::read(file_descriptor_, buffer + offset, size);
  if (result == -1) {
    throw ErrnoException(u"Failed to read file.");
  }
  return result;
}

bool UnixFileStream::CanWrite() {
  CheckClosed();
  return flags_ & OpenFileFlags::Write;
}

Index UnixFileStream::Write(const std::byte *buffer, Index offset, Index size) {
  CheckClosed();
  auto result = ::write(file_descriptor_, buffer + offset, size);
  if (result == -1) {
    throw ErrnoException(u"Failed to write file.");
  }
  return result;
}

void UnixFileStream::Close() {
  if (closed_) return;
  if (::close(file_descriptor_) == -1) {
    throw ErrnoException(u"Failed to close file.");
  }
  closed_ = true;
}

void UnixFileStream::CheckClosed() {
  if (closed_) {
    throw Exception(u"File is closed.");
  }
}
}  // namespace cru::io