libutil++  1.9.3
 All Classes Functions Variables
GZCSVFileReader.cpp
1 /*
2 ** libutil++
3 ** $Id: GZCSVFileReader.cpp 1653 2016-02-28 19:54:59Z sella $
4 ** Copyright (c) 2011-2016 Digital Genesis, LLC. All Rights Reserved.
5 ** Released under the LGPL Version 2.1 License.
6 ** http://www.digitalgenesis.com
7 */
8 
9 static const char rcsid[] __attribute__((used)) = "$Id: GZCSVFileReader.cpp 1653 2016-02-28 19:54:59Z sella $";
10 
11 #include "GZCSVFileReader.h"
12 
13 #include <fcntl.h>
14 
15 using namespace sella::file;
16 
24 GZCSVFileReader::GZCSVFileReader(const std::string & file, char delimiter, char seperator) THROWS(IOException) :
25  filename(file),
26 
27  delimiter(delimiter),
28  seperator(seperator),
29 
30  descriptor(::open(file.c_str(), O_RDONLY | O_LARGEFILE))
31 {
32  /* Ensure the file was opened for reading, and zero all buffers and state machines. */
33  if (this->descriptor <= 0) {
34  THROW(IOException, "failed to open file '%s' for reading", this->filename.c_str());
35  }
36  memset(&(this->stream), 0, sizeof(this->stream));
37  memset(&(this->buffer), 0, sizeof(this->buffer));
38 
39  /* Initialize for the reading of GZIP compressed files. */
40  if (::inflateInit2(&(this->stream), 16 + MAX_WBITS) != Z_OK) {
41  ::close(this->descriptor);
42 
43  THROW(IOException, "failed to initilize zlib instance for reading file '%s'", this->filename.c_str());
44  }
45  this->stream.avail_out = sizeof(this->buffer[1]);
46 }
47 
53  if (this->descriptor != 0) {
54  ::inflateEnd(&(this->stream));
55 
56  this->close();
57  }
58 }
59 
67 void GZCSVFileReader::read(std::vector<std::vector<std::string>> & data) THROWS(IOException) {
68  do {
69  if ((data.empty() == false) && data.back().empty()) {
70  data.pop_back();
71  }
72  data.push_back(std::vector<std::string>());
73  } while (this->read(data.back()) == true);
74 
75  if ((data.empty() == false) && data.back().empty()) {
76  data.pop_back();
77  }
78 }
79 
91 bool GZCSVFileReader::read(std::vector<std::string> & data) THROWS(IOException) {
92  /* Read field entries from the memory buffer, and as necessary fall back to reading from the disk. */
93  while (this->read(data, std::string()) == false) {
94  /* EMPTY LOOP */
95  }
96  return data.empty() ? false : true;
97 }
98 
106 void GZCSVFileReader::close(bool unlink) NO_EXCEPTIONS {
107  ::close(this->descriptor);
108 
109  if (unlink == true) {
110  ::unlink(this->filename.c_str());
111  }
112  this->filename.clear();
113  this->descriptor = 0;
114 }
115 
133 bool GZCSVFileReader::read(std::vector<std::string> & data, std::string prepend) THROWS(IOException) {
134  uint8_t * head = this->stream.next_out - (sizeof(this->buffer[1]) - this->stream.avail_out);
135 
136  /* Determine if there is decopmressed data available. */
137  if (this->stream.avail_out == sizeof(this->buffer[1])) {
138  /* There is no decopressed data available, so determine if there is compressed data available in memory. */
139  if (this->stream.avail_in == 0) {
140  /* There is no compressed data available, so read data from file into the compressed data buffer. */
141  if ((this->stream.avail_in = ::read(this->descriptor, this->buffer[0], sizeof(this->buffer[0]))) < 0) {
142  THROW(IOException, "failed to read from file '%s'", this->filename.c_str());
143  } else if (this->stream.avail_in == 0) {
144  return true; /* The end of the file was reached, and no more data could be read. */
145  }
146  this->stream.next_in = this->buffer[0];
147  }
148 
149  /* Decompress data from the compressed buffer into the uncompressed buffer. */
150  this->stream.avail_out = sizeof(this->buffer[1]);
151  this->stream.next_out = this->buffer[1];
152 
153  switch (::inflate(&(this->stream), Z_SYNC_FLUSH)) {
154  case Z_STREAM_ERROR:
155  THROW(IOException, "zlib decompression state was destroyed");
156 
157  case Z_NEED_DICT:
158  THROW(IOException, "zlib decompression failed due to missing dictionary");
159 
160  case Z_DATA_ERROR:
161  THROW(IOException, "zlib decompression failed due to bad data in stream");
162 
163  case Z_MEM_ERROR:
164  THROW(IOException, "zlib decompression failed due to a memory error");
165  }
166  head = this->stream.next_out - (sizeof(this->buffer[1]) - this->stream.avail_out);
167  }
168 
169  /* Adjust the limits of the decompressed memory buffer. */
170  for (uint8_t * i = head; i != this->stream.next_out; i++) {
171  if ((*i) == this->seperator) { /* The end of the record has been reached; store the last field and return success. */
172  data.push_back(prepend.append(std::string(reinterpret_cast<char *>(head), i - head)));
173  this->stream.avail_out += (i - head + 1);
174 
175  return true; /* The record request has been fullfilled. */
176  } else if (*(i) == this->delimiter) { /* A record delimiter has been identified. */
177  data.push_back(prepend.append(std::string(reinterpret_cast<char *>(head), i - head)));
178  this->stream.avail_out += (i - head + 1);
179 
180  return false; /* The record request has not yet been fullfilled. */
181  }
182  }
183  this->stream.avail_out = sizeof(this->buffer[1]);
184 
185  /* Recursively call this function with a prepend value supplied to account for a field which is too long for the buffers. */
186  return this->read(data, std::string(reinterpret_cast<char *>(head), this->stream.next_out - head));
187 }
188 
189 /*
190 ** vim: noet ts=3 sw=3
191 */
virtual ~GZCSVFileReader() NO_EXCEPTIONS
GZCSVFileReader(const std::string &file, char delimiter=DefaultDelimiter, char seperator=DefaultSeperator) THROWS(IOException)
void close(bool unlink=false) NO_EXCEPTIONS
void read(std::vector< std::vector< std::string >> &data) THROWS(IOException)