rapidyaml 0.15.2
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
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
14namespace c4 {
15namespace yml {
16
17C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4996) // fopen: this function may be unsafe
18C4_SUPPRESS_WARNING_CLANG_WITH_PUSH("-Wdeprecated-declarations") // fopen is deprecated
19
20/** @cond dev */
21namespace detail {
22struct 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 */
57inline 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 */
65template<class ContiguousContainer>
66void 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 */
74inline 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 */
81template<class ContiguousContainer>
82void 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 */
106inline 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);
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 */
119C4_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 */
135C4_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 */
144template<class ContiguousContainer>
145void 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 */
164template<class ContiguousContainer>
165ContiguousContainer 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 */
192template<class ContiguousContainer>
193void 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;
220errsize:
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 */
226template<class ContiguousContainer>
227ContiguousContainer stdin_get_contents(FILE *f=stdin)
228{
229 ContiguousContainer cc;
230 stdin_get_contents(&cc, f);
231 return cc;
232}
233
234/** @} */
235
236C4_SUPPRESS_WARNING_CLANG_POP
237C4_SUPPRESS_WARNING_MSVC_POP
238
239
240} // namespace yml
241} // namespace c4
242
243#endif /* _C4_YML_FILE_HPP_ */
Error utilities used by ryml.
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
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
bool read(ConstNodeRef const &n, T *v)
Definition node.hpp:1739
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
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition common.cpp:14