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