1 #include <stdio.h>
2
3 #ifdef _WIN32
4 #include <io.h>
5 #else // _WIN32
6 #include <unistd.h>
7 #endif // _WIN32
8
9 #include <limits>
10
11 #include "marisa/grimoire/io/writer.h"
12
13 namespace marisa {
14 namespace grimoire {
15 namespace io {
16
Writer()17 Writer::Writer()
18 : file_(NULL), fd_(-1), stream_(NULL), needs_fclose_(false) {}
19
~Writer()20 Writer::~Writer() {
21 if (needs_fclose_) {
22 ::fclose(file_);
23 }
24 }
25
open(const char * filename)26 void Writer::open(const char *filename) {
27 MARISA_THROW_IF(filename == NULL, MARISA_NULL_ERROR);
28
29 Writer temp;
30 temp.open_(filename);
31 swap(temp);
32 }
33
open(std::FILE * file)34 void Writer::open(std::FILE *file) {
35 MARISA_THROW_IF(file == NULL, MARISA_NULL_ERROR);
36
37 Writer temp;
38 temp.open_(file);
39 swap(temp);
40 }
41
open(int fd)42 void Writer::open(int fd) {
43 MARISA_THROW_IF(fd == -1, MARISA_CODE_ERROR);
44
45 Writer temp;
46 temp.open_(fd);
47 swap(temp);
48 }
49
open(std::ostream & stream)50 void Writer::open(std::ostream &stream) {
51 Writer temp;
52 temp.open_(stream);
53 swap(temp);
54 }
55
clear()56 void Writer::clear() {
57 Writer().swap(*this);
58 }
59
swap(Writer & rhs)60 void Writer::swap(Writer &rhs) {
61 marisa::swap(file_, rhs.file_);
62 marisa::swap(fd_, rhs.fd_);
63 marisa::swap(stream_, rhs.stream_);
64 marisa::swap(needs_fclose_, rhs.needs_fclose_);
65 }
66
seek(std::size_t size)67 void Writer::seek(std::size_t size) {
68 MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
69 if (size == 0) {
70 return;
71 } else if (size <= 16) {
72 const char buf[16] = {};
73 write_data(buf, size);
74 } else {
75 const char buf[1024] = {};
76 do {
77 const std::size_t count = (size < sizeof(buf)) ? size : sizeof(buf);
78 write_data(buf, count);
79 size -= count;
80 } while (size != 0);
81 }
82 }
83
is_open() const84 bool Writer::is_open() const {
85 return (file_ != NULL) || (fd_ != -1) || (stream_ != NULL);
86 }
87
open_(const char * filename)88 void Writer::open_(const char *filename) {
89 std::FILE *file = NULL;
90 #ifdef _MSC_VER
91 MARISA_THROW_IF(::fopen_s(&file, filename, "wb") != 0, MARISA_IO_ERROR);
92 #else // _MSC_VER
93 file = ::fopen(filename, "wb");
94 MARISA_THROW_IF(file == NULL, MARISA_IO_ERROR);
95 #endif // _MSC_VER
96 file_ = file;
97 needs_fclose_ = true;
98 }
99
open_(std::FILE * file)100 void Writer::open_(std::FILE *file) {
101 file_ = file;
102 }
103
open_(int fd)104 void Writer::open_(int fd) {
105 fd_ = fd;
106 }
107
open_(std::ostream & stream)108 void Writer::open_(std::ostream &stream) {
109 stream_ = &stream;
110 }
111
write_data(const void * data,std::size_t size)112 void Writer::write_data(const void *data, std::size_t size) {
113 MARISA_THROW_IF(!is_open(), MARISA_STATE_ERROR);
114 if (size == 0) {
115 return;
116 } else if (fd_ != -1) {
117 while (size != 0) {
118 #ifdef _WIN32
119 static const std::size_t CHUNK_SIZE =
120 std::numeric_limits<int>::max();
121 const unsigned int count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
122 const int size_written = ::_write(fd_, data, count);
123 #else // _WIN32
124 static const std::size_t CHUNK_SIZE =
125 std::numeric_limits< ::ssize_t>::max();
126 const ::size_t count = (size < CHUNK_SIZE) ? size : CHUNK_SIZE;
127 const ::ssize_t size_written = ::write(fd_, data, count);
128 #endif // _WIN32
129 MARISA_THROW_IF(size_written <= 0, MARISA_IO_ERROR);
130 data = static_cast<const char *>(data) + size_written;
131 size -= size_written;
132 }
133 } else if (file_ != NULL) {
134 MARISA_THROW_IF(::fwrite(data, 1, size, file_) != size, MARISA_IO_ERROR);
135 MARISA_THROW_IF(::fflush(file_) != 0, MARISA_IO_ERROR);
136 } else if (stream_ != NULL) {
137 try {
138 MARISA_THROW_IF(!stream_->write(static_cast<const char *>(data), size),
139 MARISA_IO_ERROR);
140 } catch (const std::ios_base::failure &) {
141 MARISA_THROW(MARISA_IO_ERROR, "std::ios_base::failure");
142 }
143 }
144 }
145
146 } // namespace io
147 } // namespace grimoire
148 } // namespace marisa
149