File Coverage

src/buffer.c
Criterion Covered Total %
statement 39 70 55.7
branch 8 26 30.7
condition n/a
subroutine n/a
pod n/a
total 47 96 48.9


line stmt bran cond sub pod time code
1             // Derived from:
2              
3             /* $OpenBSD: buffer.c,v 1.31 2006/08/03 03:34:41 deraadt Exp $ */
4             /*
5             * Author: Tatu Ylonen <ylo@cs.hut.fi>
6             * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
7             * All rights reserved
8             * Functions for manipulating fifo buffers (that can grow if needed).
9             *
10             * As far as I am concerned, the code I have written for this software
11             * can be used freely for any purpose. Any derived versions of this
12             * software must be clearly marked as such, and if the derived work is
13             * incompatible with the protocol description in the RFC file, it must be
14             * called by a name other than "ssh" or "Secure Shell".
15             */
16              
17             #include "buffer.h"
18              
19             #define BUFFER_MAX_CHUNK 0x1400000
20             #define BUFFER_MAX_LEN 0x1400000
21             #define BUFFER_ALLOCSZ 0x002000
22             #define BUFFER_COMPACT_PERCENT 0.8
23              
24             /* Initializes the buffer structure. */
25              
26             void
27 3           buffer_init(Buffer *buffer, uint32_t len)
28             {
29 3 50         if (!len) len = BUFFER_ALLOCSZ;
30              
31 3           buffer->alloc = 0;
32 3           New(0, buffer->buf, (int)len, u_char);
33 3           buffer->alloc = len;
34 3           buffer->offset = 0;
35 3           buffer->end = 0;
36              
37             #ifdef XS_DEBUG
38             PerlIO_printf(PerlIO_stderr(), "Buffer allocated with %d bytes\n", len);
39             #endif
40 3           }
41              
42             /* Frees any memory used for the buffer. */
43              
44             void
45 3           buffer_free(Buffer *buffer)
46             {
47 3 50         if (buffer->alloc > 0) {
48             #ifdef XS_DEBUG
49             PerlIO_printf(PerlIO_stderr(), "Buffer high water mark: %d\n", buffer->alloc);
50             #endif
51 3           memset(buffer->buf, 0, buffer->alloc);
52 3           buffer->alloc = 0;
53 3           Safefree(buffer->buf);
54             }
55 3           }
56              
57             /* Appends data to the buffer, expanding it if necessary. */
58              
59             void
60 104           buffer_append(Buffer *buffer, const void *data, uint32_t len)
61             {
62             void *p;
63 104           p = buffer_append_space(buffer, len);
64 104           Copy(data, p, (int)len, u_char);
65 104           }
66              
67             static int
68 0           buffer_compact(Buffer *buffer)
69             {
70             /*
71             * If the buffer is at least BUFFER_COMPACT_PERCENT empty, move the
72             * data to the beginning.
73             */
74 0 0         if (buffer->offset * 1.0 / buffer->alloc >= BUFFER_COMPACT_PERCENT ) {
75             #ifdef XS_DEBUG
76             PerlIO_printf(PerlIO_stderr(), "Buffer compacting (%d -> %d)\n", buffer->offset + buffer_len(buffer), buffer_len(buffer));
77             #endif
78 0           Move(buffer->buf + buffer->offset, buffer->buf, (int)(buffer->end - buffer->offset), u_char);
79 0           buffer->end -= buffer->offset;
80 0           buffer->offset = 0;
81 0           return (1);
82             }
83              
84             return (0);
85             }
86              
87             /*
88             * Appends space to the buffer, expanding the buffer if necessary. This does
89             * not actually copy the data into the buffer, but instead returns a pointer
90             * to the allocated region.
91             */
92              
93             void *
94 104           buffer_append_space(Buffer *buffer, uint32_t len)
95             {
96             uint32_t newlen;
97             void *p;
98              
99 104 50         if (len > BUFFER_MAX_CHUNK)
100 0           croak("buffer_append_space: len %u too large (max %u)", len, BUFFER_MAX_CHUNK);
101              
102             /* If the buffer is empty, start using it from the beginning. */
103 104 100         if (buffer->offset == buffer->end) {
104 79           buffer->offset = 0;
105 79           buffer->end = 0;
106             }
107              
108 25           restart:
109             /* If there is enough space to store all data, store it now. */
110 104 50         if (buffer->end + len <= buffer->alloc) {
111 104           p = buffer->buf + buffer->end;
112 104           buffer->end += len;
113 104           return p;
114             }
115              
116             /* Compact data back to the start of the buffer if necessary */
117 0 0         if (buffer_compact(buffer))
118 0           goto restart;
119              
120             /* Increase the size of the buffer and retry. */
121 0 0         if (buffer->alloc + len < 4096)
122 0           newlen = (buffer->alloc + len) * 2;
123             else
124 0           newlen = buffer->alloc + len + 4096;
125            
126 0 0         if (newlen > BUFFER_MAX_LEN)
127 0           croak("buffer_append_space: alloc %u too large (max %u)",
128             newlen, BUFFER_MAX_LEN);
129             #ifdef XS_DEBUG
130             PerlIO_printf(PerlIO_stderr(), "Buffer extended to %d\n", newlen);
131             #endif
132 0           Renew(buffer->buf, (int)newlen, u_char);
133 0           buffer->alloc = newlen;
134 0           goto restart;
135             /* NOTREACHED */
136             }
137              
138             /* Returns the number of bytes of data in the buffer. */
139              
140             uint32_t
141 161           buffer_len(Buffer *buffer)
142             {
143 161           return buffer->end - buffer->offset;
144             }
145              
146             /* Gets data from the beginning of the buffer. */
147              
148             int
149 0           buffer_get_ret(Buffer *buffer, void *buf, uint32_t len)
150             {
151 0 0         if (len > buffer->end - buffer->offset) {
152 0           warn("buffer_get_ret: trying to get more bytes %d than in buffer %d", len, buffer->end - buffer->offset);
153 0           return (-1);
154             }
155              
156 0           Copy(buffer->buf + buffer->offset, buf, (int)len, char);
157 0           buffer->offset += len;
158 0           return (0);
159             }
160              
161             void
162 0           buffer_get(Buffer *buffer, void *buf, uint32_t len)
163             {
164 0 0         if (buffer_get_ret(buffer, buf, len) == -1)
165 0           croak("buffer_get: buffer error");
166 0           }
167              
168             /* Consumes the given number of bytes from the beginning of the buffer. */
169              
170             int
171 151           buffer_consume_ret(Buffer *buffer, uint32_t bytes)
172             {
173 151 50         if (bytes > buffer->end - buffer->offset) {
174 0           warn("buffer_consume_ret: trying to get more bytes %d than in buffer %d", bytes, buffer->end - buffer->offset);
175 0           return (-1);
176             }
177              
178 151           buffer->offset += bytes;
179 151           return (0);
180             }
181              
182             void
183 151           buffer_consume(Buffer *buffer, uint32_t bytes)
184             {
185 151 50         if (buffer_consume_ret(buffer, bytes) == -1)
186 0           croak("buffer_consume: buffer error");
187 151           }
188              
189             /* Returns a pointer to the first used byte in the buffer. */
190              
191             void *
192 151           buffer_ptr(Buffer *buffer)
193             {
194 151           return buffer->buf + buffer->offset;
195             }
196              
197             // Dumps the contents of the buffer to stderr.
198             // Based on: http://sws.dett.de/mini/hexdump-c/
199             #ifdef XS_DEBUG
200             void
201             buffer_dump(Buffer *buffer, uint32_t size)
202             {
203             unsigned char *data = buffer->buf;
204             unsigned char c;
205             int i = 1;
206             int n;
207             char bytestr[4] = {0};
208             char hexstr[ 16*3 + 5] = {0};
209             char charstr[16*1 + 5] = {0};
210            
211             if (!size) {
212             size = buffer->end - buffer->offset;
213             }
214            
215             for (n = buffer->offset; n < buffer->offset + size; n++) {
216             c = data[n];
217              
218             /* store hex str (for left side) */
219             snprintf(bytestr, sizeof(bytestr), "%02x ", c);
220             strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);
221              
222             /* store char str (for right side) */
223             if (isalnum(c) == 0) {
224             c = '.';
225             }
226             snprintf(bytestr, sizeof(bytestr), "%c", c);
227             strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);
228              
229             if (i % 16 == 0) {
230             /* line completed */
231             PerlIO_printf(PerlIO_stderr(), "%-50.50s %s\n", hexstr, charstr);
232             hexstr[0] = 0;
233             charstr[0] = 0;
234             }
235             i++;
236             }
237              
238             if (strlen(hexstr) > 0) {
239             /* print rest of buffer if not empty */
240             PerlIO_printf(PerlIO_stderr(), "%-50.50s %s\n", hexstr, charstr);
241             }
242             }
243             #endif