1 /* buffer.c
2  *
3  * Wiretap Library
4  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5  *
6  * SPDX-License-Identifier: GPL-2.0-or-later
7  */
8 #include "config.h"
9 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
10 
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include "buffer.h"
15 #include <wsutil/ws_assert.h>
16 #include <wsutil/wslog.h>
17 
18 #define SMALL_BUFFER_SIZE (2 * 1024) /* Everyone still uses 1500 byte frames, right? */
19 static GPtrArray *small_buffers = NULL; /* Guaranteed to be at least SMALL_BUFFER_SIZE */
20 /* XXX - Add medium and large buffers? */
21 
22 /* Initializes a buffer with a certain amount of allocated space */
23 void
ws_buffer_init(Buffer * buffer,gsize space)24 ws_buffer_init(Buffer* buffer, gsize space)
25 {
26 	ws_assert(buffer);
27 	if (G_UNLIKELY(!small_buffers)) small_buffers = g_ptr_array_sized_new(1024);
28 
29 	if (space <= SMALL_BUFFER_SIZE) {
30 		if (small_buffers->len > 0) {
31 			buffer->data = (guint8*) g_ptr_array_remove_index(small_buffers, small_buffers->len - 1);
32 			ws_assert(buffer->data);
33 		} else {
34 			buffer->data = (guint8*)g_malloc(SMALL_BUFFER_SIZE);
35 		}
36 		buffer->allocated = SMALL_BUFFER_SIZE;
37 	} else {
38 		buffer->data = (guint8*)g_malloc(space);
39 		buffer->allocated = space;
40 	}
41 	buffer->start = 0;
42 	buffer->first_free = 0;
43 }
44 
45 /* Frees the memory used by a buffer */
46 void
ws_buffer_free(Buffer * buffer)47 ws_buffer_free(Buffer* buffer)
48 {
49 	ws_assert(buffer);
50 	if (buffer->allocated == SMALL_BUFFER_SIZE) {
51 		ws_assert(buffer->data);
52 		g_ptr_array_add(small_buffers, buffer->data);
53 	} else {
54 		g_free(buffer->data);
55 	}
56 	buffer->allocated = 0;
57 	buffer->data = NULL;
58 }
59 
60 /* Assures that there are 'space' bytes at the end of the used space
61 	so that another routine can copy directly into the buffer space. After
62 	doing that, the routine will also want to run
63 	ws_buffer_increase_length(). */
64 void
ws_buffer_assure_space(Buffer * buffer,gsize space)65 ws_buffer_assure_space(Buffer* buffer, gsize space)
66 {
67 	ws_assert(buffer);
68 	gsize available_at_end = buffer->allocated - buffer->first_free;
69 	gsize space_used;
70 	gboolean space_at_beginning;
71 
72 	/* If we've got the space already, good! */
73 	if (space <= available_at_end) {
74 		return;
75 	}
76 
77 	/* Maybe we don't have the space available at the end, but we would
78 		if we moved the used space back to the beginning of the
79 		allocation. The buffer could have become fragmented through lots
80 		of calls to ws_buffer_remove_start(). I'm using buffer->start as the
81 		same as 'available_at_start' in this comparison. */
82 
83 	/* or maybe there's just no more room. */
84 
85 	space_at_beginning = buffer->start >= space;
86 	if (space_at_beginning || buffer->start > 0) {
87 		space_used = buffer->first_free - buffer->start;
88 		/* this memory copy better be safe for overlapping memory regions! */
89 		memmove(buffer->data, buffer->data + buffer->start, space_used);
90 		buffer->start = 0;
91 		buffer->first_free = space_used;
92 	}
93 	/*if (buffer->start >= space) {*/
94 	if (space_at_beginning) {
95 		return;
96 	}
97 
98 	/* We'll allocate more space */
99 	buffer->allocated += space + 1024;
100 	buffer->data = (guint8*)g_realloc(buffer->data, buffer->allocated);
101 }
102 
103 void
ws_buffer_append(Buffer * buffer,guint8 * from,gsize bytes)104 ws_buffer_append(Buffer* buffer, guint8 *from, gsize bytes)
105 {
106 	ws_assert(buffer);
107 	ws_buffer_assure_space(buffer, bytes);
108 	memcpy(buffer->data + buffer->first_free, from, bytes);
109 	buffer->first_free += bytes;
110 }
111 
112 void
ws_buffer_remove_start(Buffer * buffer,gsize bytes)113 ws_buffer_remove_start(Buffer* buffer, gsize bytes)
114 {
115 	ws_assert(buffer);
116 	if (buffer->start + bytes > buffer->first_free) {
117 		ws_error("ws_buffer_remove_start trying to remove %" G_GINT64_MODIFIER "u bytes. s=%" G_GINT64_MODIFIER "u ff=%" G_GINT64_MODIFIER "u!\n",
118 			(guint64)bytes, (guint64)buffer->start,
119 			(guint64)buffer->first_free);
120 		/** ws_error() does an abort() and thus never returns **/
121 	}
122 	buffer->start += bytes;
123 
124 	if (buffer->start == buffer->first_free) {
125 		buffer->start = 0;
126 		buffer->first_free = 0;
127 	}
128 }
129 
130 
131 #ifndef SOME_FUNCTIONS_ARE_DEFINES
132 void
ws_buffer_clean(Buffer * buffer)133 ws_buffer_clean(Buffer* buffer)
134 {
135 	ws_assert(buffer);
136 	ws_buffer_remove_start(buffer, ws_buffer_length(buffer));
137 }
138 #endif
139 
140 #ifndef SOME_FUNCTIONS_ARE_DEFINES
141 void
ws_buffer_increase_length(Buffer * buffer,gsize bytes)142 ws_buffer_increase_length(Buffer* buffer, gsize bytes)
143 {
144 	ws_assert(buffer);
145 	buffer->first_free += bytes;
146 }
147 #endif
148 
149 #ifndef SOME_FUNCTIONS_ARE_DEFINES
150 gsize
ws_buffer_length(Buffer * buffer)151 ws_buffer_length(Buffer* buffer)
152 {
153 	ws_assert(buffer);
154 	return buffer->first_free - buffer->start;
155 }
156 #endif
157 
158 #ifndef SOME_FUNCTIONS_ARE_DEFINES
159 guint8 *
ws_buffer_start_ptr(Buffer * buffer)160 ws_buffer_start_ptr(Buffer* buffer)
161 {
162 	ws_assert(buffer);
163 	return buffer->data + buffer->start;
164 }
165 #endif
166 
167 #ifndef SOME_FUNCTIONS_ARE_DEFINES
168 guint8 *
ws_buffer_end_ptr(Buffer * buffer)169 ws_buffer_end_ptr(Buffer* buffer)
170 {
171 	ws_assert(buffer);
172 	return buffer->data + buffer->first_free;
173 }
174 #endif
175 
176 #ifndef SOME_FUNCTIONS_ARE_DEFINES
177 void
ws_buffer_append_buffer(Buffer * buffer,Buffer * src_buffer)178 ws_buffer_append_buffer(Buffer* buffer, Buffer* src_buffer)
179 {
180 	ws_assert(buffer);
181 	ws_buffer_append(buffer, ws_buffer_start_ptr(src_buffer), ws_buffer_length(src_buffer));
182 }
183 #endif
184 
185 void
ws_buffer_cleanup(void)186 ws_buffer_cleanup(void)
187 {
188 	if (small_buffers) {
189 		g_ptr_array_set_free_func(small_buffers, g_free);
190 		g_ptr_array_free(small_buffers, TRUE);
191 		small_buffers = NULL;
192 	}
193 }
194 
195 /*
196  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
197  *
198  * Local variables:
199  * c-basic-offset: 8
200  * tab-width: 8
201  * indent-tabs-mode: t
202  * End:
203  *
204  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
205  * :indentSize=8:tabSize=8:noTabs=false:
206  */
207