summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'libcutils/record_stream.cpp')
-rw-r--r--libcutils/record_stream.cpp187
1 files changed, 187 insertions, 0 deletions
diff --git a/libcutils/record_stream.cpp b/libcutils/record_stream.cpp
new file mode 100644
index 000000000..5a86b8393
--- /dev/null
+++ b/libcutils/record_stream.cpp
@@ -0,0 +1,187 @@
1/* libs/cutils/record_stream.c
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include <cutils/record_stream.h>
19
20#include <stdlib.h>
21#include <unistd.h>
22#include <assert.h>
23#include <errno.h>
24#include <string.h>
25#include <stdint.h>
26#if defined(_WIN32)
27#include <winsock2.h> /* for ntohl */
28#else
29#include <netinet/in.h>
30#endif
31
32#define HEADER_SIZE 4
33
34struct RecordStream {
35 int fd;
36 size_t maxRecordLen;
37
38 unsigned char *buffer;
39
40 unsigned char *unconsumed;
41 unsigned char *read_end;
42 unsigned char *buffer_end;
43};
44
45
46extern RecordStream *record_stream_new(int fd, size_t maxRecordLen)
47{
48 RecordStream *ret;
49
50 assert (maxRecordLen <= 0xffff);
51
52 ret = (RecordStream *)calloc(1, sizeof(RecordStream));
53
54 ret->fd = fd;
55 ret->maxRecordLen = maxRecordLen;
56 ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);
57
58 ret->unconsumed = ret->buffer;
59 ret->read_end = ret->buffer;
60 ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;
61
62 return ret;
63}
64
65
66extern void record_stream_free(RecordStream *rs)
67{
68 free(rs->buffer);
69 free(rs);
70}
71
72
73/* returns NULL; if there isn't a full record in the buffer */
74static unsigned char * getEndOfRecord (unsigned char *p_begin,
75 unsigned char *p_end)
76{
77 size_t len;
78 unsigned char * p_ret;
79
80 if (p_end < p_begin + HEADER_SIZE) {
81 return NULL;
82 }
83
84 //First four bytes are length
85 len = ntohl(*((uint32_t *)p_begin));
86
87 p_ret = p_begin + HEADER_SIZE + len;
88
89 if (p_end < p_ret) {
90 return NULL;
91 }
92
93 return p_ret;
94}
95
96static void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)
97{
98 unsigned char *record_start, *record_end;
99
100 record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);
101
102 if (record_end != NULL) {
103 /* one full line in the buffer */
104 record_start = p_rs->unconsumed + HEADER_SIZE;
105 p_rs->unconsumed = record_end;
106
107 *p_outRecordLen = record_end - record_start;
108
109 return record_start;
110 }
111
112 return NULL;
113}
114
115/**
116 * Reads the next record from stream fd
117 * Records are prefixed by a 16-bit big endian length value
118 * Records may not be larger than maxRecordLen
119 *
120 * Doesn't guard against EINTR
121 *
122 * p_outRecord and p_outRecordLen may not be NULL
123 *
124 * Return 0 on success, -1 on fail
125 * Returns 0 with *p_outRecord set to NULL on end of stream
126 * Returns -1 / errno = EAGAIN if it needs to read again
127 */
128int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord,
129 size_t *p_outRecordLen)
130{
131 void *ret;
132
133 ssize_t countRead;
134
135 /* is there one record already in the buffer? */
136 ret = getNextRecord (p_rs, p_outRecordLen);
137
138 if (ret != NULL) {
139 *p_outRecord = ret;
140 return 0;
141 }
142
143 // if the buffer is full and we don't have a full record
144 if (p_rs->unconsumed == p_rs->buffer
145 && p_rs->read_end == p_rs->buffer_end
146 ) {
147 // this should never happen
148 //ALOGE("max record length exceeded\n");
149 assert (0);
150 errno = EFBIG;
151 return -1;
152 }
153
154 if (p_rs->unconsumed != p_rs->buffer) {
155 // move remainder to the beginning of the buffer
156 size_t toMove;
157
158 toMove = p_rs->read_end - p_rs->unconsumed;
159 if (toMove) {
160 memmove(p_rs->buffer, p_rs->unconsumed, toMove);
161 }
162
163 p_rs->read_end = p_rs->buffer + toMove;
164 p_rs->unconsumed = p_rs->buffer;
165 }
166
167 countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);
168
169 if (countRead <= 0) {
170 /* note: end-of-stream drops through here too */
171 *p_outRecord = NULL;
172 return countRead;
173 }
174
175 p_rs->read_end += countRead;
176
177 ret = getNextRecord (p_rs, p_outRecordLen);
178
179 if (ret == NULL) {
180 /* not enough of a buffer to for a whole command */
181 errno = EAGAIN;
182 return -1;
183 }
184
185 *p_outRecord = ret;
186 return 0;
187}