Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtmail / libDtMail / Common / Buffer.C
1 /*
2  *+SNOTICE
3  *
4  *
5  *  $XConsortium: Buffer.C /main/6 1996/04/21 19:47:10 drk $
6  *
7  *  RESTRICTED CONFIDENTIAL INFORMATION:
8  *  
9  *  The information in this document is subject to special
10  *  restrictions in a confidential disclosure agreement between
11  *  HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
12  *  document outside HP, IBM, Sun, USL, SCO, or Univel without
13  *  Sun's specific written approval.  This document and all copies
14  *  and derivative works thereof must be returned or destroyed at
15  *  Sun's request.
16  *
17  *  Copyright 1993, 1994, 1995 Sun Microsystems, Inc.  All rights reserved.
18  *
19  *+ENOTICE
20  */
21
22 #include <DtMail/Buffer.hh>
23
24 #include <string.h>
25 #ifdef __osf__
26 #include <stdlib.h>
27 #else
28 #if defined(USL) && (OSMAJORVERSION == 2)
29 extern "C" {
30 #endif
31 #include <malloc.h>
32 #if defined(USL) && (OSMAJORVERSION == 2)
33 };
34 #endif
35 #endif // __osf__
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <DtMail/Threads.hh>
39
40 const int Buffer::defaultchunksize = 16 * 1024;
41
42
43 // buffer.C -- implement buffer class
44
45
46 // initialization
47 void
48 BufferMemory::initBuffer(int chunksize)
49 {
50     _firstreader = NULL;
51     _firstchunk = NULL;
52     _lastchunk = NULL;
53     _totalsize = 0;
54     _chunksize = chunksize;
55
56     _mutex = MutexInit();
57 }
58
59 BufferMemory::BufferMemory()
60 {
61     initBuffer(defaultchunksize);
62 }
63
64 BufferMemory::BufferMemory(int chunksize)
65 {
66     initBuffer(chunksize);
67 }
68
69
70 BufferMemory::~BufferMemory()
71 {
72         MutexLock mutex(_mutex);
73
74         // free the data in the buffer
75         Chunk *c = _firstchunk;
76         while (c) {
77                 Chunk *next;
78
79                 next = c->_nextchunk;
80                 free(c->_buffer);
81                 free(c);
82                 c = next;
83         }
84
85
86         // disassociate us from all the readers
87         BufReaderMemory *r = _firstreader;
88         while (r) {
89                 r->_buffer = NULL;
90                 r = r->_nextreader;
91         }
92
93         // free the mutex
94         mutex.unlock_and_destroy();
95 }
96
97
98
99
100 Buffer::Buffer() { }
101 Buffer::~Buffer() { }
102 BufReader::~BufReader() { }
103
104
105 // buffer memory class functions
106 int
107 BufferMemory::appendData(const char *user_buffer, int length)
108 {
109     int numwritten = 0;
110     int numbytes;
111
112     MutexLock mutex(_mutex);
113
114     while (numwritten < length) {
115         if (! _lastchunk || _lastchunk->_chunksize <= _lastchunk->_currentend) {
116             // we need to get a new data chunk
117             if (newChunk(length - numwritten) < 0) {
118
119                 // we had an allocation error
120                 return (numwritten);
121             }
122         }
123
124         // number of free bytes in data chunk
125         numbytes = _lastchunk->_chunksize - _lastchunk->_currentend;
126
127         // don't write more than remaining bytes in user's request
128         if (numbytes > length - numwritten) {
129             numbytes = length - numwritten;
130         }
131
132         // do the copy
133         (void) memcpy(&_lastchunk->_buffer[_lastchunk->_currentend],
134                 user_buffer, numbytes);
135
136         // update the counters
137         numwritten += numbytes;
138         _totalsize += numbytes;
139         _lastchunk->_currentend += numbytes;
140         user_buffer += numbytes;
141
142     }
143
144     return (numwritten);
145 }
146
147
148 // add a new data chunk to the buffer
149 int
150 BufferMemory::newChunk(int size)
151 {
152     // make sure we get at least the default chunksize
153     if (size < _chunksize) size = _chunksize;
154
155     // allocate the data regions
156     Chunk *d = (Chunk *) malloc(sizeof (Chunk));
157     if (! d) return (-1);
158
159     d->_buffer = (char *) malloc(size);
160     if (! d->_buffer) {
161         free (d);
162         return (-1);
163     }
164
165     d->_chunksize = size;
166     d->_currentend = 0;
167
168     // now splice it into the list
169     // lock should already have been called
170
171     d->_nextchunk = NULL;
172     if (_firstchunk == NULL) _firstchunk = d;
173
174     if (_lastchunk) {
175         _lastchunk->_nextchunk = d;
176     }
177     _lastchunk = d;
178
179     return (0);
180 }
181
182
183 BufReader *
184 BufferMemory::getReader(void)
185 {
186     BufReader *r;
187
188     MutexLock mutex(_mutex);
189
190     r = new BufReaderMemory(this);
191
192     return (r);
193 }
194
195
196 unsigned long
197 BufferMemory::iterate(Buffer::CallBack callback, ...)
198 {
199     va_list va;
200     MutexLock mutex(_mutex);
201     Chunk *c;
202     unsigned long ret = 0;
203  
204     va_start(va, callback);
205  
206     // walk through the chunk list, calling the callback for each one
207     for (c = _firstchunk; c; c = c->_nextchunk) {
208
209         // don't bother with null length chunks
210         if (c->_currentend == 0) continue;
211
212         // do the callback
213         ret = (*callback)(c->_buffer, c->_currentend, va);
214
215         // check for problems
216         if (ret) break;
217     }
218  
219     return (ret);
220 }
221  
222
223
224
225 //------------- beginning of bufreader code
226
227 BufReader::BufReader() {}
228
229 BufReaderMemory::BufReaderMemory(BufferMemory *b)
230 {
231
232     MutexLock mutex(b->_mutex);
233
234     _buffer = b;
235     _nextreader = b->_firstreader;
236     _prevreader = NULL;
237
238     if (b->_firstreader) {
239         b->_firstreader->_prevreader = this;
240     }
241     b->_firstreader = this;
242
243     _currentoffset = 0;
244     _currentchunk = NULL;
245 }
246
247
248 int
249 BufferMemory::getSize()
250 {
251     return (_totalsize);
252 }
253
254
255 int
256 BufReaderMemory::getData(char *user_buffer, int length)
257 {
258     int numread = 0;
259     int numbytes;
260
261     if (! _buffer) {
262         // the buffer has already been freed
263         return (0);
264     }
265
266     MutexLock mutex(_buffer->_mutex);
267
268     // handle starup case
269     if (_currentchunk == NULL) {
270         _currentchunk = _buffer->_firstchunk;
271
272         // see if buffer is still empty
273         if (!_currentchunk) {
274             return (0);
275         }
276     }
277
278     do {
279         // space in buffer
280         numbytes = _currentchunk->_currentend - _currentoffset;
281
282         if (numbytes <= 0) {
283             // we are at the end of this data chunk
284             if (_currentchunk->_nextchunk == NULL) {
285                 // we are at the end of the valid data
286                 break;
287             }
288
289             // advance to the next chunk
290             _currentchunk = _currentchunk->_nextchunk;
291             _currentoffset = 0;
292
293         }
294
295         // don't read more than user requested
296         if (numbytes > length - numread) {
297                 numbytes = length - numread;
298         }
299
300         // copy the data
301         memcpy(user_buffer, &_currentchunk->_buffer[_currentoffset], numbytes);
302
303         // update all the pointers
304         numread += numbytes;
305         user_buffer += numbytes;
306         _currentoffset += numbytes;
307
308     } while (numread < length);
309
310     return (numread);
311 }
312
313
314 BufReaderMemory::~BufReaderMemory()
315 {
316
317     if (! _buffer) return;
318     MutexLock mutex(_buffer->_mutex);
319
320     // unlink us from the buffer's list
321     if (_nextreader) {
322         _nextreader->_prevreader = _prevreader;
323     }
324
325     if (_prevreader) {
326             _prevreader->_nextreader = _nextreader;
327     } else {
328         // ASSERT(_buffer->_firstreader == this);
329         _buffer->_firstreader = _nextreader;
330     }
331 }
332
333
334
335
336
337
338
339
340
341
342
343
344
345