libutil++  1.9.3
 All Classes Functions Variables
GZCSVFileWriter.cpp
1 /*
2 ** libutil++
3 ** $Id: GZCSVFileWriter.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: GZCSVFileWriter.cpp 1653 2016-02-28 19:54:59Z sella $";
10 
11 #include "GZCSVFileWriter.h"
12 
13 #include <fcntl.h>
14 
15 using namespace sella::file;
16 
26 GZCSVFileWriter::GZCSVFileWriter(const std::string & file, char delimiter, char seperator) THROWS(IOException) :
27  filename(file),
28 
29  delimiter(delimiter),
30  seperator(seperator),
31 
32  descriptor(::open(file.c_str(), O_WRONLY | O_LARGEFILE | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH))
33 {
34  /* Ensure the file was opened for writing, and zero all buffers and state machines. */
35  if (this->descriptor <= 0) {
36  THROW(IOException, "failed to open file '%s' for writing", this->filename.c_str());
37  }
38  memset(&(this->stream), 0, sizeof(this->stream));
39  memset(&(this->buffer), 0, sizeof(this->buffer));
40 
41  /* Initialize for the writing of GZIP compressed files. */
42  if (::deflateInit2(&(this->stream), 9 /* Best Compression */, Z_DEFLATED, 16 + MAX_WBITS /* GZIP */, 9 /* Maximum Memory and Speed */, Z_DEFAULT_STRATEGY) != Z_OK) {
43  ::close(this->descriptor);
44 
45  THROW(IOException, "failed to initilize zlib instance for writing file '%s'", this->filename.c_str());
46  }
47 }
48 
54  if (this->descriptor != 0) {
55  ::deflateEnd(&(this->stream));
56 
57  this->close();
58  }
59 }
60 
68 void GZCSVFileWriter::write(const std::vector<std::string> & data) THROWS(IOException) {
69  size_t length = 0;
70 
71  /* Write all fields, one at a time, to the compression stream. */
72  for (auto iter = data.begin(); iter != data.end(); ++iter) {
73  ssize_t offset = 0;
74 
75  /* Append the field delimiter if this is not the first field of the record. */
76  if (iter != data.begin()) {
77  /* Purge the buffer if required to make room for the field delimiter. */
78  if (sizeof(this->delimiter) > (sizeof(this->buffer[1]) - length)) {
79  this->write(this->buffer[1], length, Z_NO_FLUSH);
80 
81  length = 0;
82  }
83  this->buffer[1][length++] = this->delimiter;
84  }
85 
86  do { /* Append the field value to the buffer. */
87  /* Copy as much of the field value as possible to the buffer. */
88  if ((iter->length() - offset) > (sizeof(this->buffer[1]) - length)) {
89  ::memcpy(&(this->buffer[1][length]), &(iter->c_str()[offset]), sizeof(this->buffer[1]) - length);
90 
91  this->write(this->buffer[1], sizeof(this->buffer[1]), Z_NO_FLUSH);
92 
93  offset += (sizeof(this->buffer[1]) - length);
94  length = 0;
95  } else {
96  ::memcpy(&(this->buffer[1][length]), &(iter->c_str()[offset]), iter->length() - offset);
97 
98  length += (iter->length() - offset);
99  offset += (iter->length() - offset);
100  }
101  } while (offset != static_cast<ssize_t>(iter->length()));
102  }
103 
104  /* Purge the buffer if required to make room for the record seperator. */
105  if (sizeof(this->seperator) > (sizeof(this->buffer[1]) - length)) {
106  this->write(this->buffer[1], length, Z_NO_FLUSH);
107 
108  length = 0;
109  }
110  this->buffer[1][length++] = this->seperator;
111 
112  this->write(this->buffer[1], length, Z_NO_FLUSH);
113 }
114 
124 void GZCSVFileWriter::write(uint8_t * input, ssize_t length, int flush) THROWS(IOException) {
125  ssize_t expect;
126 
127  this->stream.next_in = input;
128  this->stream.avail_in = length;
129 
130  do {
131  this->stream.next_out = this->buffer[0];
132  this->stream.avail_out = sizeof(this->buffer[0]);
133 
134  /* Compress the supplied data into the buffer. */
135  switch (::deflate(&(this->stream), flush)) {
136  case Z_STREAM_ERROR:
137  THROW(IOException, "zlib compression failure when writing buffer to compressed file '%s'", this->filename.c_str());
138  }
139 
140  /* Write the compressed data to the file. */
141  if ((expect = (sizeof(this->buffer[0]) - this->stream.avail_out)) > 0) {
142  while ((length = ::write(this->descriptor, this->buffer[0], sizeof(this->buffer[0]) - this->stream.avail_out)) != expect) {
143  if (length == -1) {
144  THROW(IOException, "failure writing buffer to compressed file '%s'", this->filename.c_str());
145  }
146  expect -= length;
147  }
148  }
149  } while (this->stream.avail_out == 0);
150 
151  /* Ensure all data was compressed and written to the file. */
152  if (this->stream.avail_in != 0) {
153  THROW(IOException, "failed to write full buffer to compressed file '%s'", this->filename.c_str());
154  }
155 }
156 
165 void GZCSVFileWriter::close(const std::string & rename) THROWS(IOException) {
166  uint8_t buffer[4];
167 
168  /* Close the compression stream out. */
169  this->write(buffer, 0, Z_FINISH);
170 
171  ::close(this->descriptor);
172 
173  if (rename.length() > 0) {
174  ::rename(this->filename.c_str(), rename.c_str());
175  }
176  this->filename.clear();
177  this->descriptor = 0;
178 }
179 
180 /*
181 ** vim: noet ts=3 sw=3
182 */
virtual ~GZCSVFileWriter() NO_EXCEPTIONS
void write(const std::vector< std::string > &data) THROWS(IOException)
void close(const std::string &rename=std::string()) THROWS(IOException)
GZCSVFileWriter(const std::string &file, char delimiter=DefaultDelimiter, char seperator=DefaultSeperator) THROWS(IOException)