NVIDIA Index example code nvidia_logo_transpbg.gif Up
large_file_io.cpp
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright 2023 NVIDIA Corporation. All rights reserved.
3 *****************************************************************************/
4
5#include "large_file_io.h"
6
7#include <cassert>
8#include <fstream>
9
10#include <nv/index/app/forwarding_logger.h>
11
12#ifdef _WIN32
13# ifndef WIN32_LEAN_AND_MEAN
14# define WIN32_LEAN_AND_MEAN 1
15# endif
16# include <windows.h>
17#else // _WIN32
18# include <sys/types.h>
19# include <sys/stat.h>
20# include <errno.h>
21# include <fcntl.h>
22# include <unistd.h>
23#endif // _WIN32
24
25#include <zlib.h>
26
27namespace nv {
28namespace index_common {
29namespace io {
30// namespace util for io
31namespace util {
32// check file exists
33bool file_exists(const std::string& file_path)
34{
35 using namespace std;
36 ifstream f(file_path.c_str(), ios_base::in);
37
38 return f.is_open();
39}
40
41} // namespace util
42
43#ifdef _WIN32
44
45class File::File_impl
46{
47public:
48 File_impl()
49 {
50 m_file_handle = INVALID_HANDLE_VALUE;
51 m_position = 0;
52 m_file_size = 0;
53 m_open_mode = static_cast<std::ios_base::openmode>(0);
55 }
56
57 virtual ~File_impl()
58 {
59 close();
60 }
61
62 virtual bool open(
63 const std::string& file_path,
64 std::ios_base::openmode open_mode,
65 mi::Uint32 file_flags)
66 {
67 // translate open mode to access mode
68 DWORD desired_access = 0;
69 DWORD read_write_buffer_access = 0;
70
71 if (open_mode & std::ios_base::in) {
72 desired_access |= GENERIC_READ;
73 read_write_buffer_access = PAGE_READWRITE;
74 }
75 if (open_mode & std::ios_base::out) {
76 desired_access |= GENERIC_WRITE;
77 read_write_buffer_access = PAGE_READWRITE;
78 }
79
80 // share mode
81 DWORD share_mode = FILE_SHARE_READ;
82
83 // translate open mode to creation modes
84 DWORD creation_disposition = 0;
85
87 if ( ((open_mode & std::ios_base::out)
88 || (open_mode & std::ios_base::in))
89 && !(open_mode & std::ios_base::trunc)) {
90 creation_disposition = OPEN_ALWAYS;
91 }
92 else if ( (open_mode & std::ios_base::out)
93 && (open_mode & std::ios_base::trunc)) {
94 creation_disposition = TRUNCATE_EXISTING;
95 }
96 else {
97 DEBUG_LOG << "File::open(): "
98 << "illegal open mode 0x" << std::hex << open_mode << "on existing file "
99 << "'" << file_path << "'";
100 return false;
101 }
102 }
103 else {
104 if ( (open_mode & std::ios_base::out)
105 //&& !(open_mode & std::ios_base::in)
106 //&& !(open_mode & std::ios_base::trunc)
107 )
108 {
109 creation_disposition = CREATE_NEW;
110 }
111 else {
112 //DEBUG_LOG << "File::open(): "
113 // << "illegal open mode 0x" << std::hex << open_mode << " on non existing file "
114 // << "'" << file_path << "'";
115 return false;
116 }
117 }
118
119 // file attributes
120 DWORD flags_and_attributes = FILE_ATTRIBUTE_NORMAL;
121 if ((file_flags & FILE_FLAG_SEQUENTIAL_READ) != 0) {
122 flags_and_attributes |= FILE_FLAG_SEQUENTIAL_SCAN;
123 }
124 //flags_and_attributes |= FILE_FLAG_NO_BUFFERING| FILE_FLAG_OVERLAPPED;
125
126 // use shared pointer to manage the close in case we miss it somehow
127 m_file_handle = CreateFile(file_path.c_str(),
128 desired_access,
129 share_mode,
130 0,
131 creation_disposition,
132 flags_and_attributes,
133 0);
134
135 if (m_file_handle == INVALID_HANDLE_VALUE) {
136 DEBUG_LOG << "File::open(): "
137 << "error creating/opening file: "
138 << "'" << file_path << "'";
139 return false;
140 }
141
142 if ( (open_mode & std::ios_base::ate)
143 || (open_mode & std::ios_base::app)) {
144
146 }
147
149 m_open_mode = open_mode;
150 m_file_flags = file_flags;
152
153 return true;
154 }
155
156 virtual bool is_open() const
157 {
158 if (m_file_handle == INVALID_HANDLE_VALUE) {
159 return false;
160 }
161 else {
162 return true;
163 }
164 }
165
166 virtual void close()
167 {
168 if (is_open()) {
169 CloseHandle(m_file_handle);
170 }
171
172 m_file_handle = INVALID_HANDLE_VALUE;
173 m_position = 0;
174 m_file_size = 0;
175 m_open_mode = static_cast<std::ios_base::openmode>(0);
176 m_file_path.clear();
177 }
178
179 virtual mi::Uint64 read(
180 void* output_buffer,
181 mi::Uint64 start_position,
182 mi::Uint64 num_bytes_to_read)
183 {
184 if (!is_open()) {
185 DEBUG_LOG << "File::read(): "
186 << "read access on invalid file handle.";
187 return 0;
188 }
189
190 using namespace mi;
191
192 Uint8* output_byte_buffer = reinterpret_cast<Uint8*>(output_buffer);
193 Uint64 bytes_read = 0;
194
195 m_position = start_position;
196
197 while (bytes_read < num_bytes_to_read) {
198 static const Uint64 read_max_block_size = 512ull * 1024 * 1024;
199 const mi::Uint64 file_bytes_to_read = mi::math::clamp(num_bytes_to_read - bytes_read, 0ull, read_max_block_size);
200
201 if (!set_file_pointer(m_position)) {
202 DEBUG_LOG << "File::read(): "
203 << "unable to set file pointer to current position.";
204 return 0;
205 }
206
207 DWORD file_bytes_read = 0;
208 if (ReadFile(m_file_handle, output_byte_buffer + bytes_read, static_cast<DWORD>(file_bytes_to_read), &file_bytes_read, 0) == 0) {
209 DEBUG_LOG << "File::read(): "
210 << "error reading from file " << m_file_path;
211 return 0;
212 }
213 if (file_bytes_read == 0) {
214 // eof
215 break;
216 }
217 else {
218 m_position += file_bytes_read;
219 bytes_read += file_bytes_read;
220 }
221 }
222
223 return bytes_read;
224 }
225
226 virtual mi::Uint64 read(
227 void* output_buffer,
228 mi::Uint64 num_bytes_to_read)
229 {
230 return read(output_buffer, m_position, num_bytes_to_read);
231 }
232
233 virtual mi::Uint64 write(
234 const void* input_buffer,
235 mi::Uint64 start_position,
236 mi::Uint64 num_bytes_to_write)
237 {
238 using namespace mi;
239
240 if (!is_open()) {
241 DEBUG_LOG << "File::write(): "
242 << "write access on invalid file handle.";
243 return 0;
244 }
245
246 if (m_open_mode & std::ios_base::app) {
248 }
249
250 const Uint8* input_byte_buffer = reinterpret_cast<const Uint8*>(input_buffer);
251 Uint64 bytes_written = 0;
252
253 m_position = start_position;
254
255 while (bytes_written < num_bytes_to_write) {
256 static const Uint64 write_max_block_size = 512ull * 1024 * 1024;
257 const Uint64 file_bytes_to_write = mi::math::clamp(num_bytes_to_write - bytes_written, 0ull, write_max_block_size);
258
259 if (!set_file_pointer(m_position)) {
260 return 0;
261 }
262
263 DWORD file_bytes_written = 0;
264 if (WriteFile(m_file_handle,
265 input_byte_buffer + bytes_written,
266 static_cast<DWORD>(file_bytes_to_write),
267 &file_bytes_written,
268 0) == 0)
269 {
270 DEBUG_LOG << "File::write(): "
271 << "error writing to file " << m_file_path;
272 return 0;
273 }
274
275 if (file_bytes_written == 0) {
276 break; // nothing was written
277 }
278 else {
279 m_position += file_bytes_written;
280 bytes_written += file_bytes_written;
281 }
282 }
283
284 return bytes_written;
285 }
286
287 virtual mi::Uint64 write(
288 const void* input_buffer,
289 mi::Uint64 num_bytes_to_write)
290 {
291 return write(input_buffer, m_position, num_bytes_to_write);
292 }
293
294 virtual bool flush_buffers() const
295 {
296 if (!is_open()) {
297 DEBUG_LOG << "File::flush_buffers(): "
298 << "write access on invalid file handle.";
299 return false;
300 }
301 else {
302 return FlushFileBuffers(m_file_handle) != FALSE ? true : false;
303 }
304 }
305
306 virtual mi::Uint64 size() const
307 {
308 return m_file_size;
309 }
310
311 virtual const std::string& file_path() const
312 {
313 return m_file_path;
314 }
315
316 virtual mi::Uint64 actual_file_size() const
317 {
318 assert(m_file_handle != INVALID_HANDLE_VALUE);
319
320 LARGE_INTEGER cur_size_li;
321
322 if (GetFileSizeEx(m_file_handle, &cur_size_li) == 0) {
323 DEBUG_LOG << "File::actual_file_size(): "
324 << "error retrieving current file size: " << m_file_path;
325 return 0;
326 }
327
328 return static_cast<mi::Uint64>(cur_size_li.QuadPart);
329 }
330
331 virtual bool set_file_pointer(mi::Uint64 new_pos)
332 {
333 assert(m_file_handle != INVALID_HANDLE_VALUE);
334
335 LARGE_INTEGER position_li;
336 position_li.QuadPart = new_pos;
337
338 if ( SetFilePointer(m_file_handle, position_li.LowPart, &position_li.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER
339 && GetLastError() != NO_ERROR) {
340 DEBUG_LOG << "File::set_file_pointer(): "
341 << "error setting file pointer to position "
342 << std::hex << new_pos
343 << " on file '" << m_file_path << "'";
344 return false;
345 }
346 return true;
347 }
348
349protected:
350 HANDLE m_file_handle;
351
352 mi::Uint64 m_position;
353
354 std::string m_file_path;
355 mi::Uint64 m_file_size;
356
357 std::ios_base::openmode m_open_mode;
358 mi::Uint32 m_file_flags;
359
360}; // class File::File_impl
361
362#else // _WIN32
363
364// Implementation of internal class of File class
366{
367public:
369 {
370 m_file_handle = -1;
371 m_position = 0;
372 m_file_size = 0;
373 m_open_mode = static_cast<std::ios_base::openmode>(0);
375 }
376
377 virtual ~File_impl()
378 {
379 }
380
381 virtual bool open(
382 const std::string& file_path,
383 std::ios_base::openmode open_mode,
384 mi::Uint32 file_flags)
385 {
386 using namespace mi;
387
388 mi::Sint32 open_flags = 0;
389#ifdef LINUX
390 open_flags |= O_LARGEFILE; // yes, we mainly go through this pain for large files
391 //open_flags |= O_DIRECT; // requires sector-aligned read
392 //open_flags |= O_SYNC;
393#endif
394
395 mode_t create_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
396
397 if ( (open_mode & std::ios_base::in)
398 && (open_mode & std::ios_base::out)) {
399 open_flags |= O_RDWR;
400 }
401 else if (open_mode & std::ios_base::in) {
402 open_flags |= O_RDONLY;
403 }
404 else if (open_mode & std::ios_base::out) {
405 open_flags |= O_WRONLY;
406 }
407 else {
408 DEBUG_LOG << "File::open(): "
409 << "illegal open mode 0x" << std::hex << open_mode
410 << "'" << file_path << "'";
411 return false;
412 }
413
415 if ( ((open_mode & std::ios_base::out)
416 || (open_mode & std::ios_base::in))
417 && !(open_mode & std::ios_base::trunc)) {
418 // everything ok
419 }
420 else if ( (open_mode & std::ios_base::out)
421 && (open_mode & std::ios_base::trunc)) {
422 open_flags |= O_TRUNC;
423 }
424 else {
425 DEBUG_LOG << "File::open(): "
426 << "illegal open mode 0x" << std::hex << open_mode << "on existing file "
427 << "'" << file_path << "'";
428 return false;
429 }
430 }
431 else {
432 if (open_mode & std::ios_base::out) {
433 open_flags |= O_CREAT;
434 }
435 else {
436 return false;
437 }
438 }
439
440#ifdef LINUX
441 if ((file_flags & FILE_FLAG_NO_SYSTEM_CACHE) != 0) {
442 open_flags |= O_DIRECT;
443 }
444#endif
445
446 m_file_handle = ::open(file_path.c_str(), open_flags, create_mode);
447#ifdef LINUX
448 if ((file_flags & FILE_FLAG_SEQUENTIAL_READ) != 0) {
449 posix_fadvise(m_file_handle, 0, 0, POSIX_FADV_SEQUENTIAL);
450 }
451#endif
452 //posix_fadvise(m_file_handle, 0, 0, POSIX_FADV_DONTNEED); // worsens perf, do not use!
453 //posix_fadvise(m_file_handle, 0, 0, POSIX_FADV_NOREUSE); // not implemented on Ubuntu
454
455 if (m_file_handle < 0) {
456 std::string ret_error;
457 switch (errno) {
458 case EACCES: ret_error.assign("requested access to the file is not allowed (insufficient permissions?)"); break;
459 case EEXIST: ret_error.assign("pathname already exists and O_CREAT and O_EXCL were used"); break;
460 case EFAULT: ret_error.assign("pathname points outside your accessible address space"); break;
461 case EISDIR: ret_error.assign("pathname refers to a directory and the access requested involved writing"); break;
462 default: ret_error.assign("unknown error"); break;
463 }
464 DEBUG_LOG << "File::open(): "
465 << "error opening file "
466 << "(" << ret_error << ")"
467 << " '" << file_path << "'";
468 return false;
469 }
470
471
472 if ( (open_mode & std::ios_base::ate)
473 || (open_mode & std::ios_base::app)) {
474
476 }
477
479 m_open_mode = open_mode;
480 m_file_flags = file_flags;
482
483 return true;
484 }
485
486 virtual bool is_open() const
487 {
488 return m_file_handle > -1;
489 }
490
491 virtual void close()
492 {
493 if (is_open()) {
494 if (0 != ::close(m_file_handle)) {
495 std::string ret_error;
496 switch (errno) {
497 case EBADF: ret_error.assign("invalid file descriptor"); break;
498 case EINTR: ret_error.assign("close interrupted by signal"); break;
499 case EIO: ret_error.assign("I/O error"); break;
500 default: ret_error.assign("unknown error"); break;
501 }
502 DEBUG_LOG << "File::close(): "
503 << "error closing file "
504 << "(" << ret_error << ")"
505 << " '" << m_file_path << "'";
506 }
507 }
508
509 m_file_handle = -1;
510 m_position = 0;
511 m_file_size = 0;
512 m_open_mode = static_cast<std::ios_base::openmode>(0);
513 m_file_path.clear();
514 }
515
516 mi::Uint64 read_plain(void* output_buffer,
517 mi::Uint64 start_position,
518 mi::Uint64 num_bytes_to_read)
519 {
520 using namespace mi;
521
522 Uint8* output_byte_buffer = reinterpret_cast<Uint8*>(output_buffer);
523 Uint64 bytes_read = 0;
524
525 if (num_bytes_to_read <= 0) {
526 return 0;
527 }
528
529 Uint64 start_pos = start_position;
530
531 while (bytes_read < num_bytes_to_read)
532 {
533 const Uint64 file_bytes_to_read = num_bytes_to_read - bytes_read;
534 const ssize_t file_bytes_read = ::pread(m_file_handle, output_byte_buffer + bytes_read, file_bytes_to_read, start_pos);
535
536 if (file_bytes_read < 0) {
537 DEBUG_LOG << "File::read(): "
538 << "error reading from file " << m_file_path;
539 return 0;
540 }
541 else if (file_bytes_read == 0) {
542 break; // end of file
543 }
544 else {
545 start_pos += file_bytes_read;
546 m_position += file_bytes_read; // not thread-safe!
547 bytes_read += file_bytes_read;
548 }
549 }
550
551 return bytes_read;
552 }
553
554 mi::Uint64 read_odirect(void* output_buffer,
555 mi::Uint64 start_position,
556 mi::Uint64 num_bytes_to_read)
557 {
558 const mi::Size vpage_size = 512; // #todo: use sysconf(_SC_PAGESIZE)
559 // void* obuf_next_aligned = (void*)(((mi::Size)output_buffer + vpage_size - 1) & ~(vpage_size - 1));
560
561 // new idea: the application has to conform to the aplignment properties
562 // * we cannot handle the case transparently where the buffer address and the start_position are not
563 // aligned to the page size
564 // * introduce checks and warnings if the input does not meet these criterions.
565
566 const bool is_obuf_aligned = ((mi::Size)output_buffer % vpage_size) == 0;
567 if (!is_obuf_aligned) {
568 WARN_LOG << "File::read(): " << "output_buffer address not page-aligned (non-buffered file mode).";
569 }
570
571 const bool is_spos_aligned = (start_position % vpage_size) == 0;
572 if (!is_spos_aligned) {
573 WARN_LOG << "File::read(): " << "start file-position not page-aligned (non-buffered file mode).";
574 }
575
576 return read_plain(output_buffer, start_position, num_bytes_to_read);
577 }
578
579 virtual mi::Uint64 read(void* output_buffer,
580 mi::Uint64 start_position,
581 mi::Uint64 num_bytes_to_read)
582 {
583 if (!is_open()) {
584 DEBUG_LOG << "File::read(): "
585 << "read access on invalid file handle.";
586 return 0;
587 }
588
590 return read_odirect(output_buffer, start_position, num_bytes_to_read);
591 }
592 else {
593 return read_plain(output_buffer, start_position, num_bytes_to_read);
594 }
595 }
596
597 virtual mi::Uint64 read(
598 void* output_buffer,
599 mi::Uint64 num_bytes_to_read)
600 {
601 return read(output_buffer, m_position, num_bytes_to_read);
602 }
603
604 virtual mi::Uint64 write(const void* input_buffer,
605 mi::Uint64 start_position,
606 mi::Uint64 num_bytes_to_write)
607 {
608 using namespace mi;
609
610 if (!is_open()) {
611 DEBUG_LOG << "File::write(): "
612 << "write access on invalid file handle.";
613 return 0;
614 }
615
616 if (m_open_mode & std::ios_base::app) {
618 }
619
620 const Uint8* input_byte_buffer = reinterpret_cast<const Uint8*>(input_buffer);
621 Uint64 bytes_written = 0;
622
623 m_position = start_position;
624
625 while (bytes_written < num_bytes_to_write)
626 {
627 const Uint64 file_bytes_to_write = num_bytes_to_write - bytes_written;
628 const ssize_t file_bytes_written = ::pwrite(m_file_handle, input_byte_buffer + bytes_written, file_bytes_to_write, m_position);
629
630 if (file_bytes_written < 0) {
631 DEBUG_LOG << "File::write(): "
632 << "error writing to file " << m_file_path;
633 return 0;
634 }
635 else if (file_bytes_written == 0) {
636 break; // nothing was written
637 }
638 else {
639 m_position += file_bytes_written;
640 bytes_written += file_bytes_written;
641 }
642 }
643
644 return bytes_written;
645 }
646
647 virtual mi::Uint64 write(
648 const void* input_buffer,
649 mi::Uint64 num_bytes_to_write)
650 {
651 return write(input_buffer, m_position, num_bytes_to_write);
652 }
653
654 virtual bool flush_buffers() const
655 {
656 if (!is_open()) {
657 DEBUG_LOG << "File::flush_buffers(): "
658 << "write access on invalid file handle.";
659 return false;
660 }
661 else {
662 return true;
663 }
664 }
665
666 virtual mi::Uint64 size() const
667 {
668 return m_file_size;
669 }
670
671 virtual const std::string& file_path() const
672 {
673 return m_file_path;
674 }
675
676 virtual mi::Uint64 actual_file_size() const
677 {
678 assert(is_open());
679
680 off_t new_pos = ::lseek(m_file_handle, 0, SEEK_END);
681
682 if (new_pos < 0) {
683 std::string ret_error;
684 switch (errno) {
685 case EBADF: ret_error.assign("invalid file descriptor"); break;
686 case EINVAL: ret_error.assign("invalid direction specified on lseek"); break;
687 case EOVERFLOW: ret_error.assign("overflow on returned offset"); break;
688 case ESPIPE: ret_error.assign("file descripor is associated with a pipe, socket, or FIFO"); break;
689 default: ret_error.assign("unknown error"); break;
690 }
691 DEBUG_LOG << "File::actual_file_size(): "
692 << "error retrieving file size "
693 << "(" << ret_error << ")"
694 << " on file '" << m_file_path << "'";
695 return 0;
696 }
697
698 return new_pos;
699 }
700
701protected:
702 mi::Sint32 m_file_handle;
703
704 mi::Uint64 m_position;
705
706 std::string m_file_path;
707 mi::Uint64 m_file_size;
708 mi::Uint32 m_file_flags;
709
710 std::ios_base::openmode m_open_mode;
711
712}; // class File::File_impl
713
714#endif // _WIN32
715
716// Gzip compressed file implementation.
718{
719public:
720 File_gzip_impl(mi::Uint32 compression_level)
721 : File_impl(),
722 m_compression_level(compression_level),
723 m_gz_handle(Z_NULL)
724 {
725 }
726
728 {
729 }
730
731 virtual bool open(const std::string& file_path,
732 std::ios_base::openmode open_mode,
733 mi::Uint32 file_flags)
734 {
735 std::string open_flags;
736 if (open_mode == std::ios_base::in)
737 {
738 open_flags = "rb";
739 }
740 else if (open_mode == std::ios_base::out)
741 {
742 open_flags = "wb";
743 }
744 else
745 {
746 ERROR_LOG << "File::open(): "
747 << "illegal open mode 0x" << std::hex << open_mode
748 << " for use with gzip on file "
749 << "'" << file_path << "'";
750 return false;
751 }
752
753 std::ostringstream os;
754 os << open_flags << m_compression_level;
755
756 m_gz_handle = gzopen(file_path.c_str(), os.str().c_str());
757
758 if (m_gz_handle == Z_NULL)
759 return false;
760
762 m_open_mode = open_mode;
763
764 return true;
765 }
766
767 virtual bool is_open() const
768 {
769 return m_gz_handle != Z_NULL;
770 }
771
772 virtual void close()
773 {
774 if (is_open()) {
775 gzclose(m_gz_handle);
776 }
777
778 m_gz_handle = Z_NULL;
779 m_position = 0;
780 m_file_size = 0;
781 m_open_mode = static_cast<std::ios_base::openmode>(0);
782 m_file_path.clear();
783 }
784
785 virtual mi::Uint64 read(void* output_buffer,
786 mi::Uint64 start_position,
787 mi::Uint64 num_bytes_to_read)
788 {
789 if (!is_open()) {
790 DEBUG_LOG << "File::read(): "
791 << "read access on invalid file handle.";
792 return 0;
793 }
794
795 if (start_position != m_position)
796 {
797 ERROR_LOG << "Can't seek in gzip file, requested position is " << start_position << " "
798 << "while current position is " << m_position;
799 return 0;
800 }
801
802 using namespace mi;
803
804 Uint8* output_byte_buffer = reinterpret_cast<Uint8*>(output_buffer);
805 Uint64 bytes_read = 0;
806
807 if (num_bytes_to_read <= 0) {
808 return 0;
809 }
810
811 int file_bytes_read = 0;
812 unsigned file_bytes_to_read = static_cast<unsigned>(num_bytes_to_read);
813 file_bytes_read = gzread(m_gz_handle, output_byte_buffer, file_bytes_to_read);
814
815 if (file_bytes_read <= 0) {
816 DEBUG_LOG << "File::read(): "
817 << "error reading from file " << m_file_path;
818 return 0;
819 }
820
821 if (static_cast<Uint64>(file_bytes_read) <= num_bytes_to_read) {
822 m_position += file_bytes_read;
823 bytes_read = file_bytes_read;
824 }
825 else {
826 DEBUG_LOG << "File::read(): "
827 << "unknown error reading from file " << m_file_path;
828 return 0;
829 }
830 assert(bytes_read > 0);
831
832 return bytes_read;
833 }
834
835 virtual mi::Uint64 read(
836 void* output_buffer,
837 mi::Uint64 num_bytes_to_read)
838 {
839 return read(output_buffer, m_position, num_bytes_to_read);
840 }
841
842 virtual mi::Uint64 write(const void* input_buffer,
843 mi::Uint64 start_position,
844 mi::Uint64 num_bytes_to_write)
845 {
846 using namespace mi;
847
848 if (!is_open()) {
849 DEBUG_LOG << "File::write(): "
850 << "write access on invalid file handle.";
851 return 0;
852 }
853
854 if (start_position != m_position)
855 {
856 ERROR_LOG << "Can't seek in gzip file, requested position is " << start_position << " "
857 << "while current position is " << m_position;
858 return 0;
859 }
860
861 const Uint8* input_byte_buffer = reinterpret_cast<const Uint8*>(input_buffer);
862 Uint64 bytes_written = 0;
863
864 int file_bytes_written = 0;
865 unsigned file_bytes_to_write = static_cast<unsigned>(num_bytes_to_write);
866 file_bytes_written = gzwrite(m_gz_handle, input_byte_buffer, file_bytes_to_write);
867
868 if (file_bytes_written <= 0) {
869 DEBUG_LOG << "File::write(): "
870 << "error writing to file " << m_file_path;
871 return 0;
872 }
873
874 if (static_cast<Uint64>(file_bytes_written) <= num_bytes_to_write) {
875 m_position += file_bytes_written;
876 bytes_written = file_bytes_written;
877 }
878 else {
879 DEBUG_LOG << "File::write(): "
880 << "unknown error writing to file " << m_file_path;
881 return 0;
882 }
883
884 return bytes_written;
885 }
886
887 virtual mi::Uint64 write(
888 const void* input_buffer,
889 mi::Uint64 num_bytes_to_write)
890 {
891 return write(input_buffer, m_position, num_bytes_to_write);
892 }
893
894 virtual bool flush_buffers() const
895 {
896 if (!is_open()) {
897 DEBUG_LOG << "File::flush_buffers(): "
898 << "write access on invalid file handle.";
899 return false;
900 }
901 else {
902 gzflush(m_gz_handle, Z_SYNC_FLUSH);
903 return true;
904 }
905 }
906
907 virtual mi::Uint64 size() const
908 {
909 ERROR_LOG << "size() no implemented for gzip";
910 return m_file_size;
911 }
912
913 virtual const std::string& file_path() const
914 {
915 return m_file_path;
916 }
917
918 virtual mi::Uint64 actual_file_size() const
919 {
920 ERROR_LOG << "actual_file_size() no implemented for gzip";
921 return 0;
922 }
923
924protected:
927
928}; // class File::File_gzip_impl
929
931{
932 m_file_impl = new File_impl();
933}
934
935File::File(mi::Uint32 gzip_compression_level)
936{
937 if (gzip_compression_level == 0)
938 {
939 m_file_impl = new File_impl();
940 return;
941 }
942
943 m_file_impl = new File_gzip_impl(gzip_compression_level);
944}
945
946File::File(const std::string& file_path,
947 std::ios_base::openmode open_mode,
948 mi::Uint32 file_flags)
949{
950 m_file_impl = new File_impl();
951
952 open(file_path, open_mode, file_flags);
953}
954
956{
957 assert(0 != m_file_impl);
958
959 close();
960 delete m_file_impl;
961}
962
963bool File::open(const std::string& file_path,
964 std::ios_base::openmode open_mode,
965 mi::Uint32 file_flags)
966{
967 assert(0 != m_file_impl);
968
969 if (is_open())
970 {
971 close();
972 }
973
974 return m_file_impl->open(file_path, open_mode, file_flags);
975}
976
977bool File::is_open() const
978{
979 assert(0 != m_file_impl);
980 return m_file_impl->is_open();
981}
982
984{
985 assert(0 != m_file_impl);
986 return m_file_impl->close();
987}
988
989mi::Uint64 File::read(void* output_buffer,
990 mi::Uint64 start_position,
991 mi::Uint64 num_bytes_to_read)
992{
993 assert(0 != m_file_impl);
994 return m_file_impl->read(output_buffer, start_position, num_bytes_to_read);
995}
996
997mi::Uint64 File::write(const void* input_buffer,
998 mi::Uint64 start_position,
999 mi::Uint64 num_bytes_to_write)
1000{
1001 assert(0 != m_file_impl);
1002 return m_file_impl->write(input_buffer, start_position, num_bytes_to_write);
1003}
1004
1005mi::Uint64 File::read(void* output_buffer,
1006 mi::Uint64 num_bytes_to_read)
1007{
1008 assert(0 != m_file_impl);
1009 return m_file_impl->read(output_buffer, num_bytes_to_read);
1010}
1011
1012mi::Uint64 File::write(const void* input_buffer,
1013 mi::Uint64 num_bytes_to_write)
1014{
1015 assert(0 != m_file_impl);
1016 return m_file_impl->write(input_buffer, num_bytes_to_write);
1017}
1018
1020{
1021 assert(0 != m_file_impl);
1022 return m_file_impl->flush_buffers();
1023}
1024
1025mi::Uint64 File::size() const
1026{
1027 assert(0 != m_file_impl);
1028 return m_file_impl->size();
1029}
1030
1031const std::string& File::file_path() const
1032{
1033 assert(0 != m_file_impl);
1034 return m_file_impl->file_path();
1035}
1036
1037File::operator bool() const
1038{
1039 return is_open();
1040}
1041
1042} // namespace io
1043}} // namespace nv::index_common
virtual mi::Uint64 actual_file_size() const
virtual mi::Uint64 write(const void *input_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_write)
virtual bool open(const std::string &file_path, std::ios_base::openmode open_mode, mi::Uint32 file_flags)
mi::Uint64 read_plain(void *output_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_read)
virtual const std::string & file_path() const
virtual mi::Uint64 write(const void *input_buffer, mi::Uint64 num_bytes_to_write)
virtual mi::Uint64 read(void *output_buffer, mi::Uint64 num_bytes_to_read)
virtual mi::Uint64 read(void *output_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_read)
virtual mi::Uint64 size() const
mi::Uint64 read_odirect(void *output_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_read)
virtual mi::Uint64 write(const void *input_buffer, mi::Uint64 num_bytes_to_write)
virtual mi::Uint64 size() const
virtual mi::Uint64 write(const void *input_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_write)
File_gzip_impl(mi::Uint32 compression_level)
virtual mi::Uint64 read(void *output_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_read)
virtual const std::string & file_path() const
virtual bool open(const std::string &file_path, std::ios_base::openmode open_mode, mi::Uint32 file_flags)
virtual mi::Uint64 read(void *output_buffer, mi::Uint64 num_bytes_to_read)
virtual mi::Uint64 actual_file_size() const
mi::Uint64 write(const void *input_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_write)
bool open(const std::string &file_path, std::ios_base::openmode open_mode=std::ios_base::in|std::ios_base::out, mi::Uint32 file_flags=FILE_FLAG_NONE)
mi::Uint64 read(void *output_buffer, mi::Uint64 start_position, mi::Uint64 num_bytes_to_read)
const std::string & file_path() const
File abstraction for transparent handling of large (>4GiB) on Linux and Windows platforms.
bool file_exists(const std::string &file_path)