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