rapidyaml  0.16.0
parseandemitYAML,anddoitfast
file.hpp
Go to the documentation of this file.
1 #ifndef C4_YML_FILE_HPP_
2 #define C4_YML_FILE_HPP_
3 
4 /** @name file.hpp Helpers to read/write files from disk */
5 
6 
7 #ifndef C4_YML_ERROR_HPP_
8 #include <c4/yml/error.hpp>
9 #endif
10 #include <stddef.h>
11 #include <stdio.h>
12 
13 
14 namespace c4 {
15 namespace yml {
16 
17 C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4996) // fopen: this function may be unsafe
18 C4_SUPPRESS_WARNING_CLANG_WITH_PUSH("-Wdeprecated-declarations") // fopen is deprecated
19 
20 /** @cond dev */
21 namespace detail {
22 struct ScopedFILE
23 {
24  FILE *file;
25  inline ScopedFILE(const char *filename, const char *access) // NOLINT
26  {
27  file = std::fopen(filename, access); // NOLINT
28  if(file == nullptr)
29  RYML_ERR_BASIC_("{}: could not open file", filename);
30  }
31  inline ~ScopedFILE() noexcept // NOLINT
32  {
33  std::fclose(file); // NOLINT
34  }
35  ScopedFILE(const ScopedFILE&) = delete;
36  ScopedFILE( ScopedFILE&&) = delete;
37  ScopedFILE& operator=(const ScopedFILE&) = delete;
38  ScopedFILE& operator=( ScopedFILE&&) = delete;
39 };
40 } // detail
41 /** @endcond */
42 
43 
44 //-----------------------------------------------------------------------------
45 
46 /** @defgroup doc_file_put_contents file_put_contents()
47  *
48  * Save a buffer to disk
49  *
50  * @ingroup doc_file_utils
51  *
52  * @addtogroup doc_file_put_contents
53  * @{
54  */
55 
56 /** save a contiguous buffer into a file */
57 inline void file_put_contents(void const* buf, size_t sz, FILE *file, const char* filename=nullptr)
58 {
59  size_t written = std::fwrite(buf, 1, sz, file); // NOLINT
60  if C4_UNLIKELY(written != sz)
61  RYML_ERR_BASIC_("{}: failed file write: expected={}B actual={}B", filename ? filename : "file", sz, written); // LCOV_EXCL_LINE
62 }
63 
64 /** save a contiguous buffer into a file */
65 template<class ContiguousContainer>
66 void file_put_contents(ContiguousContainer const& v, FILE *file, const char *filename=nullptr)
67 {
68  size_t vsz = static_cast<size_t>(v.size()) * sizeof(typename ContiguousContainer::value_type);
69  void const* vbuf = v.empty() ? nullptr : &v[0];
70  file_put_contents(vbuf, vsz, file, filename);
71 }
72 
73 /** save a contiguous buffer into a file */
74 inline void file_put_contents(void const* buf, size_t sz, const char *filename, const char* access="wb")
75 {
76  detail::ScopedFILE f(filename, access);
77  file_put_contents(buf, sz, f.file, filename);
78 }
79 
80 /** save a contiguous buffer into a file */
81 template<class ContiguousContainer>
82 void file_put_contents(ContiguousContainer const& v, const char *filename, const char* access="wb")
83 {
84  size_t vsz = static_cast<size_t>(v.size()) * sizeof(typename ContiguousContainer::value_type);
85  void const* vbuf = v.empty() ? nullptr : &v[0];
86  file_put_contents(vbuf, vsz, filename, access);
87 }
88 
89 /** @} */
90 
91 
92 //-----------------------------------------------------------------------------
93 
94 /** @defgroup doc_file_get_contents file_get_contents()
95  *
96  * Load a file from disk into a buffer.
97  *
98  * @ingroup doc_file_utils
99  *
100  * @addtogroup doc_file_get_contents
101  * @{
102  */
103 
104 /** load a file of specified size from disk into an existing contiguous buffer.
105  */
106 inline void file_get_contents(const char *filename, FILE *fp, size_t filesz, void *buf, size_t bufsz)
107 {
108  RYML_ASSERT_BASIC_(filesz <= bufsz);(void)bufsz;
109  size_t read = std::fread(buf, 1, filesz, fp); // NOLINT(clang-analyzer-unix.Errno)
110  if C4_UNLIKELY(read != filesz)
111  RYML_ERR_BASIC_("{}: failed file read: expected={}B actual={}B", filename, filesz, read); // LCOV_EXCL_LINE
112 }
113 
114 
115 /** load a file from disk into an existing contiguous buffer.
116  *
117  * @return true if the file was successfully read and the buffer was
118  * large enough to fit the file size */
119 C4_NODISCARD inline size_t file_get_contents(const char *filename, FILE *fp, void *buf, size_t bufsz)
120 {
121  std::fseek(fp, 0, SEEK_END); // NOLINT
122  size_t filesz = static_cast<size_t>(std::ftell(fp)); // NOLINT
123  std::rewind(fp); // NOLINT
124  if(filesz <= bufsz)
125  file_get_contents(filename, fp, filesz, buf, bufsz);
126  return filesz;
127 }
128 
129 
130 /** load a file from disk into an existing contiguous buffer.
131  *
132  * @return the size required for the buffer. It is up to the caller to
133  * check that the returned size is smaller than the buffer's size.
134  */
135 C4_NODISCARD inline size_t file_get_contents(const char *filename, void *buf, size_t bufsz, const char *access="rb")
136 {
137  detail::ScopedFILE f(filename, access);
138  return file_get_contents(filename, f.file, buf, bufsz);
139 }
140 
141 
142 /** load a file from disk into an existing ContiguousContainer,
143  * resizing it to fit the file's contents */
144 template<class ContiguousContainer>
145 void file_get_contents(ContiguousContainer *v, const char *filename, const char *access="rb")
146 {
147  using value_type = typename ContiguousContainer::value_type;
148  using size_type = typename ContiguousContainer::size_type;
149  detail::ScopedFILE f(filename, access);
150  void * dat = !v->empty() ? &(*v)[0] : nullptr;
151  size_t vsz = static_cast<size_t>(v->size());
152  size_t fsz = file_get_contents(filename, f.file, dat, vsz);
153  size_t num_elms = fsz / sizeof(value_type);
154  if C4_UNLIKELY(fsz != num_elms * sizeof(value_type))
155  RYML_ERR_BASIC_("{}: file size ({}B) not a multiple of element size ({}B)", filename, fsz, sizeof(value_type));
156  v->resize(static_cast<size_type>(num_elms));
157  if(fsz > vsz * sizeof(value_type))
158  file_get_contents(filename, f.file, fsz, &(*v)[0], fsz);
159 }
160 
161 
162 /** load a file from disk and return a newly created
163  * ContiguousContainer with the file contents */
164 template<class ContiguousContainer>
165 ContiguousContainer file_get_contents(const char *filename, const char *access="rb")
166 {
167  ContiguousContainer cc;
168  file_get_contents(&cc, filename, access);
169  return cc;
170 }
171 
172 
173 /** @} */
174 
175 
176 
177 //-----------------------------------------------------------------------------
178 
179 /** @defgroup doc_stdin_get_contents stdin_get_contents()
180  *
181  * Load a file from stdin (or similar stream-like file) into a buffer.
182  *
183  * @ingroup doc_file_utils
184  *
185  * @addtogroup doc_stdin_get_contents
186  * @{
187  */
188 
189 /** load a file from stdin (or similar stream-like file) and
190  * return a newly created ContiguousContainer with the file
191  * contents */
192 template<class ContiguousContainer>
193 void stdin_get_contents(ContiguousContainer *cont, FILE *f=stdin)
194 {
195  using I = typename ContiguousContainer::size_type;
196  using T = typename ContiguousContainer::value_type;
197  int c;
198  I pos = 0;
199  I num_bytes = 128;
200  I elmsz = static_cast<I>(sizeof(T));
201  I sz = (num_bytes + elmsz - 1) / elmsz; // round up to next multiple of elmsz
202  num_bytes = sz * elmsz;
203  cont->resize(sz);
204  unsigned char *buf = reinterpret_cast<unsigned char*>(&(*cont)[0]); // NOLINT
205  while((c = fgetc(f)) != EOF)
206  {
207  if(pos == num_bytes)
208  {
209  num_bytes = 2 * num_bytes;
210  if(num_bytes % sizeof(T)) goto errsize; // NOLINT // LCOV_EXCL_LINE
211  cont->resize(num_bytes / sizeof(T));
212  buf = reinterpret_cast<unsigned char*>(&(*cont)[0]); // NOLINT
213  }
214  buf[pos++] = static_cast<unsigned char>(c);
215  }
216  if(pos % sizeof(T))
217  goto errsize; // NOLINT
218  cont->resize(pos / sizeof(T));
219  return;
220 errsize:
221  RYML_ERR_BASIC_("file size is not multiple of element size");
222 }
223 
224 /** load a file from stdin and return a newly created
225  * ContiguousContainer with the file contents */
226 template<class ContiguousContainer>
227 ContiguousContainer stdin_get_contents(FILE *f=stdin)
228 {
229  ContiguousContainer cc;
230  stdin_get_contents(&cc, f);
231  return cc;
232 }
233 
234 /** @} */
235 
236 C4_SUPPRESS_WARNING_CLANG_POP
237 C4_SUPPRESS_WARNING_MSVC_POP
238 
239 
240 } // namespace yml
241 } // namespace c4
242 
243 #endif /* C4_YML_FILE_HPP_ */
void stdin_get_contents(ContiguousContainer *cont, FILE *f=stdin)
load a file from stdin (or similar stream-like file) and return a newly created ContiguousContainer w...
Definition: file.hpp:193
void file_get_contents(const char *filename, FILE *fp, size_t filesz, void *buf, size_t bufsz)
load a file of specified size from disk into an existing contiguous buffer.
Definition: file.hpp:106
Error utilities used by ryml.
ReadResult read(ConstNodeRef const &n, T *v)
Definition: node.hpp:2074
void file_put_contents(void const *buf, size_t sz, FILE *file, const char *filename=nullptr)
save a contiguous buffer into a file
Definition: file.hpp:57