summaryrefslogtreecommitdiffstats
path: root/source/lib/membuffer.c
blob: 85f63e8c06551220cde57e8dc8115d21945bc710 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   Samba memory buffer functions
   Copyright (C) Andrew Tridgell              1992-1997
   Copyright (C) Luke Kenneth Casson Leighton 1996-1997
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*******************************************************************
 *
 * Description: memory buffer / stream management.
 * Author     : Luke K C Leighton
 * Created    : Dec 1997
 *

 * this module is intended for use in streaming data in and out of
 * buffers.  it is intended that a single data stream be subdivided
 * into manageable sections.

 * for example, an rpc header contains a length field, but until the
 * data has been created, the length is unknown.  using this module,
 * the header section can be tacked onto the front of the data memory
 * list once the size of the data section preceding it is known.
 
 * the "margin" can be used to over-run and retrospectively lengthen
 * the buffer.  this is to save time in some of the loops, where it is
 * not particularly desirable to realloc data by 1, 2 or 4 bytes
 * repetitively...

 * each memory buffer contains a start and end offset.  the end of
 * one buffer should equal to the start of the next in the chain.
 * (end - start = len, instead of end - start + 1 = len)

 * the debug log levels are very high in some of the routines: you
 * have no idea how boring it gets staring at debug output from these

 ********************************************************************/


#include "includes.h"

extern int DEBUGLEVEL;

/*******************************************************************
 initialise a memory buffer.
 ********************************************************************/
void mem_init(struct mem_buf *buf, int margin)
{
	buf->dynamic   = True;
	buf->data      = NULL;
	buf->data_size = 0;
	buf->data_used = 0;

	buf->margin    = margin;

	buf->next      = NULL;

	buf->offset.start = 0;
	buf->offset.end   = 0;
}

/*******************************************************************
 initialise a memory buffer.

 dynamic indicates memory has been dynamically allocated.
 if mem_free is called, the memory will be freed.
 ********************************************************************/
void mem_create(struct mem_buf *buf, char *data, int size, int margin, BOOL dynamic)
{
	buf->dynamic   = dynamic;
	buf->data      = data;
	buf->data_size = size;
	buf->data_used = size;

	buf->margin    = margin;

	buf->next      = NULL;

	buf->offset.start = 0;
	buf->offset.end   = size;
}

/*******************************************************************
 allocate a memory buffer.  assume it's empty
 ********************************************************************/
BOOL mem_alloc_data(struct mem_buf *buf, int size)
{
	if (!buf->dynamic)
	{
		DEBUG(3,("mem_alloc_data: warning - memory buffer type is set to static\n"));
	}

	buf->data_size = size + buf->margin;
	buf->data_used = size;

	buf->data = malloc(buf->data_size);

	if (buf->data == NULL)
	{
		DEBUG(3,("mem_alloc: could not malloc size %d\n",
					  buf->data_size));
		mem_init(buf, buf->margin);

		return False;
	}

	bzero(buf->data, buf->data_size);

	return True;
}

/*******************************************************************
 allocates a memory buffer structure
 ********************************************************************/
BOOL mem_buf_copy(char *copy_into, struct mem_buf *buf,
				uint32 offset, uint32 len)
{
	uint32 end = offset + len;
	char *q = NULL;
	uint32 data_len = mem_buf_len(buf);
	uint32 start_offset = offset;
	struct mem_buf **bcp = &buf;
	
	if (buf == NULL || copy_into == NULL) return False;

	DEBUG(200,("mem_buf_copy: data[%d..%d] offset %d len %d\n",
	            buf->offset.start, data_len, offset, len));

	/* there's probably an off-by-one bug, here, and i haven't even tested the code :-) */
	while (offset < end && ((q = mem_data(bcp, offset)) != NULL))
	{
		uint32 copy_len = (*bcp)->offset.end - offset;

		DEBUG(200,("\tdata[%d..%d] - offset %d len %d\n",
		        (*bcp)->offset.start, (*bcp)->offset.end,
		        offset, copy_len));

		memcpy(copy_into, q, copy_len);
	
		offset    += copy_len;
		copy_into += copy_len;
	}

	if ((*bcp) != NULL)
	{
		DEBUG(200,("mem_buf_copy: copied %d bytes\n", offset - start_offset));
	}
	else
	{
		DEBUG(200,("mem_buf_copy: failed\n"));
	}

	return buf != NULL;
}

/*******************************************************************
 allocates a memory buffer structure
 ********************************************************************/
BOOL mem_buf_init(struct mem_buf **buf, uint32 margin)
{
	if (buf == NULL) return False;

	if ((*buf) == NULL)
	{
		(*buf) = malloc(sizeof(**buf));
		if ((*buf) != NULL) 
		{
			mem_init((*buf), margin);
			return True;
		}
	}
	else
	{
		(*buf)->margin = margin;
		return True;
	}
	return False;
}

/*******************************************************************
 frees up a memory buffer.
 ********************************************************************/
void mem_buf_free(struct mem_buf **buf)
{
	if (buf == NULL) return;
	if ((*buf) == NULL) return;

	mem_free_data(*buf);            /* delete memory data */
	free(*buf);                     /* delete item */
	(*buf) = NULL;
}

/*******************************************************************
 frees a memory buffer chain.  assumes that all items are malloced.
 ********************************************************************/
static void mem_free_chain(struct mem_buf **buf)
{
	if (buf == NULL) return;
	if ((*buf) == NULL) return;

	if ((*buf)->next != NULL)
	{
		mem_free_chain(&((*buf)->next)); /* delete all other items in chain */
	}
	mem_buf_free(buf);
}

/*******************************************************************
 frees a memory buffer.
 ********************************************************************/
void mem_free_data(struct mem_buf *buf)
{
	if (buf == NULL) return;

	if (buf->data != NULL && buf->dynamic)
	{
		free(buf->data);     /* delete data in this structure */
	}
	mem_init(buf, buf->margin);
}

/*******************************************************************
 reallocate a memory buffer, including a safety margin
 ********************************************************************/
BOOL mem_realloc_data(struct mem_buf *buf, int new_size)
{
	char *new_data;

	if (!buf->dynamic)
	{
		DEBUG(3,("mem_realloc_data: memory buffer has not been dynamically allocated!\n"));
		return False;
	}

	if (new_size == 0)
	{
		mem_free_data(buf);
		return True;
	}

	new_data = Realloc(buf->data, new_size + buf->margin);

	if (new_data != NULL)
	{
		buf->data = new_data;
		buf->data_size = new_size + buf->margin;
		buf->data_used = new_size;
	}
	else if (buf->data_size <= new_size)
	{
		DEBUG(3,("mem_realloc: warning - could not realloc to %d(+%d)\n",
				  new_size, buf->margin));

		buf->data_used = new_size;
	}
	else 
	{
		DEBUG(3,("mem_realloc: error - could not realloc to %d\n",
				  new_size));

		mem_free_data(buf);
		return False;
	}

	return True;
}

/*******************************************************************
 reallocate a memory buffer, retrospectively :-)
 ********************************************************************/
BOOL mem_grow_data(struct mem_buf **buf, BOOL io, int new_size, BOOL force_grow)
{
	if (new_size + (*buf)->margin >= (*buf)->data_size)
	{
		if (io && !force_grow)
		{
			DEBUG(3,("mem_grow_data: cannot resize when reading from a data stream\n"));
		}
		else
		{
			return mem_realloc_data((*buf), new_size);
		}
	}
	return True;
}

/*******************************************************************
 search for a memory buffer that falls within the specified offset
 ********************************************************************/
static BOOL mem_find(struct mem_buf **buf, uint32 offset)
{
	struct mem_buf *f;
	if (buf == NULL) return False;

	f = *buf;

	DEBUG(200,("mem_find: data[%d..%d] offset: %d\n",
	      f->offset.start, f->offset.end, offset));

	while (f != NULL && offset >= f->offset.end)
	{
		f = f->next;

		DEBUG(200,("mem_find: next[%d..%d]\n",
	      f->offset.start, f->offset.end));
	}

	(*buf) = f;

	DEBUG(200,("mem_find: found data[%d..%d]\n",
	      (*buf)->offset.start,(*buf)->offset.end));

	return f != NULL;
}


/*******************************************************************
 add up the lengths of all sections.
 ********************************************************************/
uint32 mem_buf_len(struct mem_buf *buf)
{
	int len = 0;
	while (buf != NULL)
	{
		len += buf->offset.end - buf->offset.start;
		buf = buf->next;
	}
	return len;
}


/*******************************************************************
 return the memory location specified by offset.  may return NULL.
 ********************************************************************/
char *mem_data(struct mem_buf **buf, uint32 offset)
{
	if (mem_find(buf, offset))
	{
		return &((*buf)->data[offset - (*buf)->offset.start]);
	}
	return NULL;
}