1/*
2 * bufExt.c --
3 *
4 *	Implementations of an extendable buffer.
5 *
6 * Copyright (c) 2000-2009 by Andreas Kupries <a.kupries@westend.com>
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * RCS: @(#) $Id: bufExt.c,v 1.3 2009/03/16 18:50:59 andreas_kupries Exp $
12 */
13
14#include "memchanInt.h"
15#include "buf.h"
16
17/*
18 * Forward declarations of all internal procedures.
19 */
20
21static int        ReadProc   _ANSI_ARGS_ ((Buf_Buffer buf, ClientData clientData,
22					   VOID* outbuf, int size));
23static int        WriteProc  _ANSI_ARGS_ ((Buf_Buffer buf, ClientData clientData,
24					   CONST VOID* inbuf, int size));
25static Buf_Buffer DupProc    _ANSI_ARGS_ ((Buf_Buffer buf, ClientData clientData));
26static void       FreeProc   _ANSI_ARGS_ ((Buf_Buffer buf, ClientData clientData));
27static int        SizeProc   _ANSI_ARGS_ ((Buf_Buffer buf, ClientData clientData));
28static int        TellProc   _ANSI_ARGS_ ((Buf_Buffer buf, ClientData clientData));
29static char*      DataProc   _ANSI_ARGS_ ((Buf_Buffer buf, ClientData clientData));
30
31/* Internal structure used to hold the buffer information.
32 */
33
34typedef struct ExtBuffer_ {
35  Buf_Buffer buf;      /* The buffer token containing this structure. */
36  int        size;     /* Size of the area for data, maximal amount of
37			* bytes storable in the buffer. */
38  char*      readLoc;  /* The location till which data was read from the
39			* buffer. */
40  char*      writeLoc; /* The location at which new data can be appended to
41			* the buffer. */
42  char*      limit;    /* A pointer behind the last character in the buffer. */
43  char*      data;     /* Pointer to start of the actual container */
44} ExtBuffer;
45
46/* Declaration of the buffer type.
47 */
48
49static Buf_BufferType ext = {
50  "extendable-buffer", /* Buffer of varying size */
51  ReadProc,            /* Reading from a buffer */
52  WriteProc,           /* Writing to a buffer */
53  DupProc,             /* Duplicating a buffer */
54  FreeProc,            /* Freeing all allocated resources of a buffer */
55  SizeProc,            /* Number of bytes currently in the buffer. */
56  TellProc,            /* Return current location */
57  DataProc             /* Return start of data */
58};
59
60
61/*
62 *------------------------------------------------------*
63 *
64 *	FreeProc --
65 *
66 *	Deallocates the resources of the buffer.
67 *
68 *	Sideeffects:
69 *		See above.
70 *
71 *	Result:
72 *		None.
73 *
74 *------------------------------------------------------*
75 */
76
77void
78FreeProc (buf, clientData)
79     Buf_Buffer buf;
80     ClientData clientData;
81{
82  ExtBuffer* iBuf = (ExtBuffer*) clientData;
83  Tcl_Free (iBuf->data);
84  Tcl_Free ((char*) iBuf);
85}
86
87/*
88 *------------------------------------------------------*
89 *
90 *	SizeProc --
91 *
92 *	Returns the number of bytes currently stored in
93 *	the buffer.
94 *
95 *	Sideeffects:
96 *		None.
97 *
98 *	Result:
99 *		See above.
100 *
101 *------------------------------------------------------*
102 */
103
104int
105SizeProc (buf, clientData)
106     Buf_Buffer buf;
107     ClientData clientData;
108{
109  ExtBuffer* iBuf = (ExtBuffer*) clientData;
110  return (iBuf->writeLoc - iBuf->readLoc);
111}
112
113/*
114 *------------------------------------------------------*
115 *
116 *	TellProc --
117 *
118 *	Returns the offset of the current read location
119 *	relative to the start of the data.
120 *
121 *	Sideeffects:
122 *		None.
123 *
124 *	Result:
125 *		See above.
126 *
127 *------------------------------------------------------*
128 */
129
130int
131TellProc (buf, clientData)
132     Buf_Buffer buf;
133     ClientData clientData;
134{
135  ExtBuffer* iBuf = (ExtBuffer*) clientData;
136  return iBuf->readLoc - iBuf->data;
137}
138
139/*
140 *------------------------------------------------------*
141 *
142 *	DataProc --
143 *
144 *	Returns the start of the data area.
145 *	(Here: Start of the data area in the underlying buffer)
146 *
147 *	Sideeffects:
148 *		None.
149 *
150 *	Result:
151 *		See above.
152 *
153 *------------------------------------------------------*
154 */
155
156char*
157DataProc (buf, clientData)
158     Buf_Buffer buf;
159     ClientData clientData;
160{
161  ExtBuffer* iBuf = (ExtBuffer*) clientData;
162  return iBuf->data;
163}
164
165/*
166 *------------------------------------------------------*
167 *
168 *	DupProc --
169 *
170 *	Duplicates a buffer and its contents.
171 *
172 *	Sideeffects:
173 *		Allocates memory.
174 *
175 *	Result:
176 *		A new buffer token.
177 *
178 *------------------------------------------------------*
179 */
180
181Buf_Buffer
182DupProc (buf, clientData)
183     Buf_Buffer buf;
184     ClientData clientData;
185{
186  ExtBuffer* iBuf   = (ExtBuffer*) clientData;
187  ExtBuffer* newBuf = (ExtBuffer*) Tcl_Alloc (sizeof(ExtBuffer) +
188					      (iBuf->limit - iBuf->data));
189  Buf_Buffer   new    = Buf_Create (&ext, (ClientData) newBuf);
190
191  newBuf->buf      = new;
192  newBuf->data     = Tcl_Alloc (iBuf->size);
193  newBuf->size     = iBuf->size;
194  newBuf->readLoc  = newBuf->data + (iBuf->readLoc  - iBuf->data);
195  newBuf->writeLoc = newBuf->data + (iBuf->writeLoc - iBuf->data);
196  newBuf->limit    = newBuf->data + newBuf->size;
197
198  if ((iBuf->writeLoc - iBuf->readLoc) > 0) {
199    /* Copy just that part of container which was not read already
200     */
201    memcpy (newBuf->readLoc, iBuf->readLoc, iBuf->writeLoc - iBuf->readLoc);
202  }
203
204  return new;
205}
206
207/*
208 *------------------------------------------------------*
209 *
210 *	ReadProc --
211 *
212 *	Reads at most size bytes from the current location
213 *	in the buffer and stores it into outbuf.
214 *
215 *	Sideeffects:
216 *		Moves the read pointer behind the bytes
217 *		just read from the buffer.
218 *
219 *	Result:
220 *		The number of bytes actually read from
221 *		the buffer.
222 *
223 *------------------------------------------------------*
224 */
225
226int
227ReadProc (buf, clientData, outbuf, size)
228     Buf_Buffer  buf;
229     ClientData  clientData;
230     VOID*       outbuf;
231     int         size;
232{
233  ExtBuffer* iBuf  = (ExtBuffer*) clientData;
234  int        bSize = iBuf->writeLoc - iBuf->readLoc;
235
236  if ((bSize <= 0) || (size <= 0)) {
237    return 0;
238  }
239
240  if (bSize < size) {
241    size = bSize;
242  }
243
244  memcpy (outbuf, iBuf->readLoc, size);
245  iBuf->readLoc += size;
246
247  return size;
248}
249
250/*
251 *------------------------------------------------------*
252 *
253 *	WriteProc --
254 *
255 *	Writes at most size bytes from inbuf and appends
256 *	it the buffer
257 *
258 *	Sideeffects:
259 *		Moves the write pointer behind the bytes
260 *		just written into the buffer.
261 *
262 *	Result:
263 *		The number of bytes actually written
264 *		into the buffer.
265 *
266 *------------------------------------------------------*
267 */
268
269int
270WriteProc (buf, clientData, inbuf, size)
271     Buf_Buffer  buf;
272     ClientData  clientData;
273     CONST void* inbuf;
274     int         size;
275{
276  ExtBuffer* iBuf  = (ExtBuffer*) clientData;
277  int        bSize = iBuf->limit - iBuf->writeLoc;
278
279  if (size <= 0) {
280    return 0;
281  }
282
283  if (bSize < size) {
284    /* Not enough memory to copy the whole input to the buffer.
285     * So extend it now.
286     */
287
288    char* ndata = (char*) Tcl_Alloc (iBuf->size + size);
289
290    memcpy (ndata, iBuf->data, iBuf->size);
291
292    iBuf->size    += size;
293    iBuf->readLoc  = ndata + (iBuf->readLoc  - iBuf->data);
294    iBuf->writeLoc = ndata + (iBuf->writeLoc - iBuf->data);
295    iBuf->limit    = ndata + iBuf->size;
296    iBuf->data     = ndata;
297  }
298
299  memcpy (iBuf->writeLoc, inbuf, size);
300  iBuf->writeLoc += size;
301
302  return size;
303}
304
305
306/*
307 * ------------------------------------------------------------
308 */
309
310Buf_Buffer
311Buf_CreateExtendableBuffer (size)
312     int size;
313{
314  ExtBuffer* newBuf = (ExtBuffer*) Tcl_Alloc (sizeof(ExtBuffer));
315  Buf_Buffer new    = Buf_Create (&ext, (ClientData) newBuf);
316
317  newBuf->buf      = new;
318  newBuf->size     = size;
319  newBuf->data     = Tcl_Alloc (size);
320  newBuf->readLoc  = newBuf->data;
321  newBuf->writeLoc = newBuf->data;
322  newBuf->limit    = newBuf->data + size;
323
324  return new;
325}
326
327