1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "hw-BufferedTextOutput"
18 
19 #include <hwbinder/BufferedTextOutput.h>
20 #include <hwbinder/Debug.h>
21 
22 #include <cutils/atomic.h>
23 #include <cutils/threads.h>
24 #include <utils/Log.h>
25 #include <utils/RefBase.h>
26 #include <utils/Vector.h>
27 
28 #include <hwbinder/Static.h>
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 // ---------------------------------------------------------------------------
34 
35 namespace android {
36 namespace hardware {
37 
38 struct BufferedTextOutput::BufferState : public RefBase
39 {
BufferStateandroid::hardware::BufferedTextOutput::BufferState40     explicit BufferState(int32_t _seq)
41         : seq(_seq)
42         , buffer(NULL)
43         , bufferPos(0)
44         , bufferSize(0)
45         , atFront(true)
46         , indent(0)
47         , bundle(0) {
48     }
~BufferStateandroid::hardware::BufferedTextOutput::BufferState49     ~BufferState() {
50         free(buffer);
51     }
52 
appendandroid::hardware::BufferedTextOutput::BufferState53     status_t append(const char* txt, size_t len) {
54         if ((len+bufferPos) > bufferSize) {
55             size_t newSize = ((len+bufferPos)*3)/2;
56             if (newSize < (len+bufferPos)) return NO_MEMORY;  // overflow
57             void* b = realloc(buffer, newSize);
58             if (!b) return NO_MEMORY;
59             buffer = (char*)b;
60             bufferSize = newSize;
61         }
62         if ((len+bufferPos) < bufferPos) return NO_MEMORY;  // integer overflow
63         memcpy(buffer+bufferPos, txt, len);
64         bufferPos += len;
65         return NO_ERROR;
66     }
67 
restartandroid::hardware::BufferedTextOutput::BufferState68     void restart() {
69         bufferPos = 0;
70         atFront = true;
71         if (bufferSize > 256) {
72             void* b = realloc(buffer, 256);
73             if (b) {
74                 buffer = (char*)b;
75                 bufferSize = 256;
76             }
77         }
78     }
79 
80     const int32_t seq;
81     char* buffer;
82     size_t bufferPos;
83     size_t bufferSize;
84     bool atFront;
85     int32_t indent;
86     int32_t bundle;
87 };
88 
89 struct BufferedTextOutput::ThreadState
90 {
91     Vector<sp<BufferedTextOutput::BufferState> > states;
92 };
93 
94 static mutex_t          gMutex;
95 
96 static thread_store_t   tls;
97 
getThreadState()98 BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState()
99 {
100     ThreadState*  ts = (ThreadState*) thread_store_get( &tls );
101     if (ts) return ts;
102     ts = new ThreadState;
103     thread_store_set( &tls, ts, threadDestructor );
104     return ts;
105 }
106 
threadDestructor(void * st)107 void BufferedTextOutput::threadDestructor(void *st)
108 {
109     delete ((ThreadState*)st);
110 }
111 
112 static volatile int32_t gSequence = 0;
113 
114 static volatile int32_t gFreeBufferIndex = -1;
115 
allocBufferIndex()116 static int32_t allocBufferIndex()
117 {
118     int32_t res = -1;
119 
120     mutex_lock(&gMutex);
121 
122     if (gFreeBufferIndex >= 0) {
123         res = gFreeBufferIndex;
124         gFreeBufferIndex = gTextBuffers[res];
125         gTextBuffers.editItemAt(res) = -1;
126 
127     } else {
128         res = gTextBuffers.size();
129         gTextBuffers.add(-1);
130     }
131 
132     mutex_unlock(&gMutex);
133 
134     return res;
135 }
136 
freeBufferIndex(int32_t idx)137 static void freeBufferIndex(int32_t idx)
138 {
139     mutex_lock(&gMutex);
140     gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
141     gFreeBufferIndex = idx;
142     mutex_unlock(&gMutex);
143 }
144 
145 // ---------------------------------------------------------------------------
146 
BufferedTextOutput(uint32_t flags)147 BufferedTextOutput::BufferedTextOutput(uint32_t flags)
148     : mFlags(flags)
149     , mSeq(android_atomic_inc(&gSequence))
150     , mIndex(allocBufferIndex())
151 {
152     mGlobalState = new BufferState(mSeq);
153     if (mGlobalState) mGlobalState->incStrong(this);
154 }
155 
~BufferedTextOutput()156 BufferedTextOutput::~BufferedTextOutput()
157 {
158     if (mGlobalState) mGlobalState->decStrong(this);
159     freeBufferIndex(mIndex);
160 }
161 
print(const char * txt,size_t len)162 status_t BufferedTextOutput::print(const char* txt, size_t len)
163 {
164     AutoMutex _l(mLock);
165     BufferState* b = getBuffer();
166     const char* const end = txt+len;
167     status_t err;
168 
169     while (txt < end) {
170         // Find the next line.
171         const char* first = txt;
172         while (txt < end && *txt != '\n') txt++;
173 
174         // Include this and all following empty lines.
175         while (txt < end && *txt == '\n') txt++;
176 
177         // Special cases for first data on a line.
178         if (b->atFront) {
179             if (b->indent > 0) {
180                 // If this is the start of a line, add the indent.
181                 const char* prefix = stringForIndent(b->indent);
182                 err = b->append(prefix, strlen(prefix));
183                 if (err != NO_ERROR) return err;
184             } else if (*(txt-1) == '\n' && !b->bundle) {
185                 // Fast path: if we are not indenting or bundling, and
186                 // have been given one or more complete lines, just write
187                 // them out without going through the buffer.
188 
189                 // Slurp up all of the lines.
190                 const char* lastLine = txt+1;
191                 while (txt < end) {
192                     if (*txt++ == '\n') lastLine = txt;
193                 }
194                 struct iovec vec;
195                 vec.iov_base = (void*)first;
196                 vec.iov_len = lastLine-first;
197                 //printf("Writing %d bytes of data!\n", vec.iov_len);
198                 writeLines(vec, 1);
199                 txt = lastLine;
200                 continue;
201             }
202         }
203 
204         // Append the new text to the buffer.
205         err = b->append(first, txt-first);
206         if (err != NO_ERROR) return err;
207         b->atFront = *(txt-1) == '\n';
208 
209         // If we have finished a line and are not bundling, write
210         // it out.
211         //printf("Buffer is now %d bytes\n", b->bufferPos);
212         if (b->atFront && !b->bundle) {
213             struct iovec vec;
214             vec.iov_base = b->buffer;
215             vec.iov_len = b->bufferPos;
216             //printf("Writing %d bytes of data!\n", vec.iov_len);
217             writeLines(vec, 1);
218             b->restart();
219         }
220     }
221 
222     return NO_ERROR;
223 }
224 
moveIndent(int delta)225 void BufferedTextOutput::moveIndent(int delta)
226 {
227     AutoMutex _l(mLock);
228     BufferState* b = getBuffer();
229     b->indent += delta;
230     if (b->indent < 0) b->indent = 0;
231 }
232 
pushBundle()233 void BufferedTextOutput::pushBundle()
234 {
235     AutoMutex _l(mLock);
236     BufferState* b = getBuffer();
237     b->bundle++;
238 }
239 
popBundle()240 void BufferedTextOutput::popBundle()
241 {
242     AutoMutex _l(mLock);
243     BufferState* b = getBuffer();
244     b->bundle--;
245     LOG_FATAL_IF(b->bundle < 0,
246         "TextOutput::popBundle() called more times than pushBundle()");
247     if (b->bundle < 0) b->bundle = 0;
248 
249     if (b->bundle == 0) {
250         // Last bundle, write out data if it is complete.  If it is not
251         // complete, don't write until the last line is done... this may
252         // or may not be the write thing to do, but it's the easiest.
253         if (b->bufferPos > 0 && b->atFront) {
254             struct iovec vec;
255             vec.iov_base = b->buffer;
256             vec.iov_len = b->bufferPos;
257             writeLines(vec, 1);
258             b->restart();
259         }
260     }
261 }
262 
getBuffer() const263 BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
264 {
265     if ((mFlags&MULTITHREADED) != 0) {
266         ThreadState* ts = getThreadState();
267         if (ts) {
268             while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL);
269             BufferState* bs = ts->states[mIndex].get();
270             if (bs != NULL && bs->seq == mSeq) return bs;
271 
272             ts->states.editItemAt(mIndex) = new BufferState(mIndex);
273             bs = ts->states[mIndex].get();
274             if (bs != NULL) return bs;
275         }
276     }
277 
278     return mGlobalState;
279 }
280 
281 }; // namespace hardware
282 }; // namespace android
283