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