NCBI C++ ToolKit
stream.c
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2  * Copyright (C) 2013 Frediano Ziglio
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 /**
21  * \file
22  * \brief Handle stream of data
23  */
24 
25 #include <config.h>
26 
27 #if HAVE_ERRNO_H
28 #include <errno.h>
29 #endif /* HAVE_ERRNO_H */
30 
31 #if HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif /* HAVE_STDLIB_H */
34 
35 #if HAVE_STRING_H
36 #include <string.h>
37 #endif /* HAVE_STRING_H */
38 
39 #if HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42 
43 #include <assert.h>
44 
45 #include <freetds/tds.h>
46 #include <freetds/iconv.h>
47 #include <freetds/stream.h>
48 
49 /** \cond HIDDEN_SYMBOLS */
50 #if ENABLE_EXTRA_CHECKS
51 # define TEMP_INIT(s) const size_t temp_size = s; char* temp = tds_new(char, temp_size)
52 # define TEMP_FREE free(temp);
53 # define TEMP_SIZE temp_size
54 #else
55 # define TEMP_INIT(s) char temp[s]
56 # define TEMP_FREE ;
57 # define TEMP_SIZE sizeof(temp)
58 #endif
59 /** \endcond */
60 
61 /**
62  * Reads and writes from a stream converting characters
63  * \tds
64  * \param char_conv conversion structure
65  * \param direction specify conversion to server or from server
66  * \param istream input stream
67  * \param ostream output stream
68  * \return TDS_SUCCESS of TDS_FAIL
69  */
70 TDSRET
72  TDSINSTREAM * istream, TDSOUTSTREAM *ostream)
73 {
74  TEMP_INIT(4096);
75  /*
76  * temp (above) is the "preconversion" buffer, the place where the UCS-2 data
77  * are parked before converting them to ASCII. It has to have a size,
78  * and there's no advantage to allocating dynamically.
79  * This also avoids any memory allocation error.
80  */
81  const char *ib;
82  size_t bufleft = 0;
83  TDSRET res = TDS_FAIL;
84 
85  /* cast away const for message suppression sub-structure */
86  TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;
87 
88  memset(suppress, 0, sizeof(char_conv->suppress));
89  for (ib = temp; ostream->buf_len; ib = temp + bufleft) {
90 
91  char *ob;
92  int len, conv_errno;
93  size_t ol;
94 
95  assert(ib >= temp);
96 
97  /* read a chunk of data */
98  len = istream->read(istream, (char*) ib, TEMP_SIZE - bufleft);
99  if (len < 0)
100  break;
101  if (len == 0 && bufleft == 0) {
102  res = TDS_SUCCESS;
103  break;
104  }
105  bufleft += len;
106 
107  /* Convert chunk */
108  ib = temp; /* always convert from start of buffer */
109 
110 convert_more:
111  ob = ostream->buffer;
112  ol = ostream->buf_len;
113  /* FIXME not for last */
114  suppress->einval = 1; /* EINVAL matters only on the last chunk. */
115  suppress->e2big = 1;
116  ol = tds_iconv(tds, char_conv, direction, (const char **) &ib, &bufleft, &ob, &ol);
117  conv_errno = errno;
118 
119  /* write converted chunk */
120  len = ostream->write(ostream, ob - ostream->buffer);
121  if (TDS_UNLIKELY(len < 0))
122  break;
123 
124  if ((size_t) -1 == ol) {
125  tdsdump_log(TDS_DBG_NETWORK, "Error: tds_convert_stream: tds_iconv returned errno %d, conv_errno %d\n", errno, conv_errno);
126  if (conv_errno == E2BIG && ostream->buf_len && bufleft && len)
127  goto convert_more;
128  if (conv_errno != EILSEQ) {
129  tdsdump_log(TDS_DBG_NETWORK, "Error: tds_convert_stream: "
130  "Gave up converting %u bytes due to error %d.\n",
131  (unsigned int) bufleft, errno);
132  tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", ib, bufleft);
133  }
134 
135  if (TDS_UNLIKELY(ib == temp)) { /* tds_iconv did not convert anything, avoid infinite loop */
136  tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: some bytes left.\n");
137  res = TDS_FAIL;
138  if (conv_errno == EINVAL && tds)
140  if (conv_errno == E2BIG && tds)
142  errno = conv_errno;
143  break;
144  }
145 
146  if (bufleft)
147  memmove(temp, ib, bufleft);
148  }
149  }
150 
151  TEMP_FREE;
152  return res;
153 }
154 
155 /**
156  * Reads and writes from a stream to another
157  * \tds
158  * \param istream input stream
159  * \param ostream output stream
160  * \return TDS_SUCCESS or TDS_FAIL
161  */
162 TDSRET
164 {
165  while (ostream->buf_len) {
166  /* read a chunk of data */
167  int len = istream->read(istream, ostream->buffer, ostream->buf_len);
168  if (len == 0)
169  return TDS_SUCCESS;
170  if (TDS_UNLIKELY(len < 0))
171  break;
172 
173  /* write chunk */
174  len = ostream->write(ostream, len);
175  if (TDS_UNLIKELY(len < 0))
176  break;
177  }
178  return TDS_FAIL;
179 }
180 
181 /**
182  * Reads data from network for input stream
183  */
184 static int
185 tds_datain_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
186 {
187  TDSDATAINSTREAM *s = (TDSDATAINSTREAM *) stream;
188  if (len > s->wire_size)
189  len = s->wire_size;
190  if (!tds_get_n(s->tds, ptr, len))
191  return -1;
192  s->wire_size -= len;
193  return len;
194 }
195 
196 /**
197  * Initialize a data input stream.
198  * This stream read data from network.
199  * \param stream input stream to initialize
200  * \tds
201  * \param wire_size byte to read
202  */
203 void
204 tds_datain_stream_init(TDSDATAINSTREAM * stream, TDSSOCKET * tds, size_t wire_size)
205 {
207  stream->wire_size = wire_size;
208  stream->tds = tds;
209 }
210 
211 /**
212  * Writes data to network for output stream
213  */
214 static int
216 {
217  TDSDATAOUTSTREAM *s = (TDSDATAOUTSTREAM *) stream;
218  TDSSOCKET *tds = s->tds;
219 
220  assert(len <= stream->buf_len);
221  assert(stream->buffer == (char *) tds->out_buf + tds->out_pos);
223 
224  tds->out_pos += len;
225  /* this must be strictly test as equal means we send a full packet
226  * and we could be just at the end of packet so server would
227  * wait for another packet with flag != 0
228  */
229  if (tds->out_pos > tds->out_buf_max)
230  tds_write_packet(tds, 0x0);
231  stream->buffer = (char *) tds->out_buf + tds->out_pos;
233  s->written += len;
234  return len;
235 }
236 
237 /**
238  * Initialize a data output stream.
239  * This stream writes data to network.
240  * \param stream output stream to initialize
241  * \tds
242  */
243 void
245 {
246 #if TDS_ADDITIONAL_SPACE < 4
247 #error Not supported
248 #endif
249  /*
250  * we use the extra space as we want possible space for converting
251  * a character and cause we don't want to send an exactly entire
252  * packet with 0 flag and then nothing
253  */
254  size_t left = tds->out_buf_max - tds->out_pos + TDS_ADDITIONAL_SPACE;
255 
256  assert(left > 0);
258  stream->stream.buffer = (char *) tds->out_buf + tds->out_pos;
259  stream->stream.buf_len = left;
260  stream->written = 0;
261  stream->tds = tds;
262 }
263 
264 /**
265  * Reads data from a static allocated buffer
266  */
267 static int
268 tds_staticin_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
269 {
270  TDSSTATICINSTREAM *s = (TDSSTATICINSTREAM *) stream;
271  size_t cp = (len <= s->buf_left) ? len : s->buf_left;
272 
273  memcpy(ptr, s->buffer, cp);
274  s->buffer += cp;
275  s->buf_left -= cp;
276  return cp;
277 }
278 
279 /**
280  * Initialize an input stream for read from a static allocated buffer
281  * \param stream stream to initialize
282  * \param ptr buffer to read from
283  * \param len buffer size in bytes
284  */
285 void
286 tds_staticin_stream_init(TDSSTATICINSTREAM * stream, const void *ptr, size_t len)
287 {
289  stream->buffer = (const char *) ptr;
290  stream->buf_left = len;
291 }
292 
293 
294 /**
295  * Writes data to a static allocated buffer
296  */
297 static int
299 {
300  assert(stream->buf_len >= len);
301  stream->buffer += len;
302  stream->buf_len -= len;
303  return len;
304 }
305 
306 /**
307  * Initialize an output stream for write into a static allocated buffer
308  * \param stream stream to initialize
309  * \param ptr buffer to write to
310  * \param len buffer size in bytes
311  */
312 void
314 {
316  stream->stream.buffer = (char *) ptr;
317  stream->stream.buf_len = len;
318 }
319 
320 /**
321  * Writes data to a dynamic allocated buffer
322  */
323 static int
325 {
326  TDSDYNAMICSTREAM *s = (TDSDYNAMICSTREAM *) stream;
327  size_t wanted;
328 
329  s->size += len;
330  /* grow linearly till some limit then exponentially */
331  if (s->size + 256 > s->allocated) {
332  wanted = s->size + (s->size < 4096 ? 1024 : s->size / 8u);
333  if (TDS_UNLIKELY(!tds_realloc(s->buf, wanted)))
334  return -1;
335  s->allocated = wanted;
336  }
337  assert(s->allocated > s->size);
338  stream->buffer = (char *) *s->buf + s->size;
339  stream->buf_len = s->allocated - s->size;
340  return len;
341 }
342 
343 /**
344  * Initialize a dynamic output stream.
345  * This stream write data into a dynamic allocated buffer.
346  * \param stream stream to initialize
347  * \param ptr pointer to pointer to buffer to fill. Buffer
348  * will be extended as needed
349  * \param allocated bytes initialially allocated for the buffer.
350  * Useful to reuse buffers
351  * \return TDS_SUCCESS on success, TDS_FAIL otherwise
352  */
353 TDSRET
354 tds_dynamic_stream_init(TDSDYNAMICSTREAM * stream, void **ptr, size_t allocated)
355 {
356  const size_t initial_size = 1024;
357 
359  stream->buf = ptr;
360  if (allocated < initial_size) {
361  free(*ptr);
362  *ptr = NULL;
363  allocated = initial_size;
364  }
365  if (!*ptr) {
366  *ptr = malloc(allocated);
367  if (TDS_UNLIKELY(!*ptr))
368  return TDS_FAIL;
369  }
370  stream->allocated = allocated;
371  stream->size = 0;
372  stream->stream.buffer = (char *) *ptr;
373  stream->stream.buf_len = allocated;
374  return TDS_SUCCESS;
375 }
376 
377 
TDS_ICONV_DIRECTION
Definition: iconv.h:70
#define EILSEQ
Definition: iconv.h:44
#define TDS_ADDITIONAL_SPACE
#define TDS_FAIL
Definition: tds.h:204
#define tdsdump_log
Definition: tds.h:1561
#define tdsdump_dump_buf
Definition: tds.h:1564
@ TDSEICONVAVAIL
Definition: tds.h:298
@ TDSEICONVIU
Definition: tds.h:297
int TDSRET
Definition: tds.h:201
#define TDS_UNLIKELY(x)
Definition: tds.h:372
#define TDS_SUCCESS
Definition: tds.h:203
#define tds_get_ctx(tds)
Definition: tds.h:1294
#define TDS_DBG_NETWORK
Definition: tds.h:901
TDSRET tds_copy_stream(TDSSOCKET *tds, TDSINSTREAM *istream, TDSOUTSTREAM *ostream)
Reads and writes from a stream to another \tds.
Definition: stream.c:163
TDSRET tds_dynamic_stream_init(TDSDYNAMICSTREAM *stream, void **ptr, size_t allocated)
Initialize a dynamic output stream.
Definition: stream.c:355
TDSRET tds_convert_stream(TDSSOCKET *tds, TDSICONV *char_conv, TDS_ICONV_DIRECTION direction, TDSINSTREAM *istream, TDSOUTSTREAM *ostream)
Reads and writes from a stream converting characters \tds.
Definition: stream.c:71
void tds_staticout_stream_init(TDSSTATICOUTSTREAM *stream, void *ptr, size_t len)
Initialize an output stream for write into a static allocated buffer.
Definition: stream.c:314
void tds_dataout_stream_init(TDSDATAOUTSTREAM *stream, TDSSOCKET *tds)
Initialize a data output stream.
Definition: stream.c:245
void tds_staticin_stream_init(TDSSTATICINSTREAM *stream, const void *ptr, size_t len)
Initialize an input stream for read from a static allocated buffer.
Definition: stream.c:287
void tds_datain_stream_init(TDSDATAINSTREAM *stream, TDSSOCKET *tds, size_t wire_size)
Initialize a data input stream.
Definition: stream.c:205
static TDSSOCKET * tds
Definition: collations.c:37
#define tds_get_n
#define tds_iconv
#define tds_realloc
#define tdserror
#define tds_write_packet
static int tds_datain_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
Reads data from network for input stream.
Definition: stream.c:185
static int tds_staticout_stream_write(TDSOUTSTREAM *stream, size_t len)
Writes data to a static allocated buffer.
Definition: stream.c:298
static int tds_staticin_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
Reads data from a static allocated buffer.
Definition: stream.c:268
static int tds_dynamic_stream_write(TDSOUTSTREAM *stream, size_t len)
Writes data to a dynamic allocated buffer.
Definition: stream.c:324
static int tds_dataout_stream_write(TDSOUTSTREAM *stream, size_t len)
Writes data to network for output stream.
Definition: stream.c:215
#define NULL
Definition: ncbistd.hpp:225
int len
#define memmove(a, b, c)
#define assert(x)
Definition: srv_diag.hpp:58
input stream to read data from tds protocol
Definition: stream.h:63
TDSSOCKET * tds
Definition: stream.h:66
size_t wire_size
bytes still to read
Definition: stream.h:65
TDSINSTREAM stream
Definition: stream.h:64
output stream to write data to tds protocol
Definition: stream.h:72
size_t written
Definition: stream.h:75
TDSSOCKET * tds
Definition: stream.h:74
TDSOUTSTREAM stream
Definition: stream.h:73
output stream to write data to a dynamic buffer
Definition: stream.h:99
TDSOUTSTREAM stream
Definition: stream.h:100
void ** buf
where is stored the pointer
Definition: stream.h:102
size_t size
size of data inside buffer
Definition: stream.h:106
size_t allocated
currently allocated buffer
Definition: stream.h:104
unsigned int einval
Definition: iconv.h:81
unsigned int e2big
Definition: iconv.h:79
define a stream of data used for input
Definition: stream.h:30
int(* read)(struct tds_input_stream *stream, void *ptr, size_t len)
read some data Return 0 if end of stream Return <0 if error (actually not defined)
Definition: stream.h:35
define a stream of data used for output
Definition: stream.h:39
size_t buf_len
Definition: stream.h:51
int(* write)(struct tds_output_stream *stream, size_t len)
write len bytes from buffer, return <0 if error or len
Definition: stream.h:41
char * buffer
write buffer.
Definition: stream.h:50
Information for a server connection.
Definition: tds.h:1211
unsigned out_pos
current position in out_buf
Definition: tds.h:1238
unsigned char * out_buf
Output buffer.
Definition: tds.h:1230
unsigned int out_buf_max
Maximum size of packet pointed by out_buf.
Definition: tds.h:1236
input stream to read data from a static buffer
Definition: stream.h:81
const char * buffer
Definition: stream.h:83
TDSINSTREAM stream
Definition: stream.h:82
size_t buf_left
Definition: stream.h:84
output stream to write data to a static buffer.
Definition: stream.h:92
TDSOUTSTREAM stream
Definition: stream.h:93
TDS_ERRNO_MESSAGE_FLAGS suppress
Definition: iconv.h:106
void free(voidpf ptr)
voidp malloc(uInt size)
Modified on Wed Sep 04 14:59:05 2024 by modify_doxy.py rev. 669887