• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 struct 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 
record_stream_new(int fd,size_t maxRecordLen)46 extern 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 
record_stream_free(RecordStream * rs)66 extern 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 */
getEndOfRecord(unsigned char * p_begin,unsigned char * p_end)74 static 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 
getNextRecord(RecordStream * p_rs,size_t * p_outRecordLen)96 static 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  */
record_stream_get_next(RecordStream * p_rs,void ** p_outRecord,size_t * p_outRecordLen)128 int 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 }
188