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 #include <binder/BufferedTextOutput.h>
18 #include <binder/Debug.h>
19
20 #include <utils/Atomic.h>
21 #include <utils/Log.h>
22 #include <utils/RefBase.h>
23 #include <utils/Vector.h>
24 #include <cutils/threads.h>
25
26 #include <private/binder/Static.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 // ---------------------------------------------------------------------------
32
33 namespace android {
34
35 struct BufferedTextOutput::BufferState : public RefBase
36 {
BufferStateandroid::BufferedTextOutput::BufferState37 explicit BufferState(int32_t _seq)
38 : seq(_seq)
39 , buffer(NULL)
40 , bufferPos(0)
41 , bufferSize(0)
42 , atFront(true)
43 , indent(0)
44 , bundle(0) {
45 }
~BufferStateandroid::BufferedTextOutput::BufferState46 ~BufferState() {
47 free(buffer);
48 }
49
appendandroid::BufferedTextOutput::BufferState50 status_t append(const char* txt, size_t len) {
51 if ((len+bufferPos) > bufferSize) {
52 size_t newSize = ((len+bufferPos)*3)/2;
53 if (newSize < (len+bufferPos)) return NO_MEMORY; // overflow
54 void* b = realloc(buffer, newSize);
55 if (!b) return NO_MEMORY;
56 buffer = (char*)b;
57 bufferSize = newSize;
58 }
59 memcpy(buffer+bufferPos, txt, len);
60 bufferPos += len;
61 return NO_ERROR;
62 }
63
restartandroid::BufferedTextOutput::BufferState64 void restart() {
65 bufferPos = 0;
66 atFront = true;
67 if (bufferSize > 256) {
68 void* b = realloc(buffer, 256);
69 if (b) {
70 buffer = (char*)b;
71 bufferSize = 256;
72 }
73 }
74 }
75
76 const int32_t seq;
77 char* buffer;
78 size_t bufferPos;
79 size_t bufferSize;
80 bool atFront;
81 int32_t indent;
82 int32_t bundle;
83 };
84
85 struct BufferedTextOutput::ThreadState
86 {
87 Vector<sp<BufferedTextOutput::BufferState> > states;
88 };
89
90 static mutex_t gMutex;
91
92 static thread_store_t tls;
93
getThreadState()94 BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState()
95 {
96 ThreadState* ts = (ThreadState*) thread_store_get( &tls );
97 if (ts) return ts;
98 ts = new ThreadState;
99 thread_store_set( &tls, ts, threadDestructor );
100 return ts;
101 }
102
threadDestructor(void * st)103 void BufferedTextOutput::threadDestructor(void *st)
104 {
105 delete ((ThreadState*)st);
106 }
107
108 static volatile int32_t gSequence = 0;
109
110 static volatile int32_t gFreeBufferIndex = -1;
111
allocBufferIndex()112 static int32_t allocBufferIndex()
113 {
114 int32_t res = -1;
115
116 mutex_lock(&gMutex);
117
118 if (gFreeBufferIndex >= 0) {
119 res = gFreeBufferIndex;
120 gFreeBufferIndex = gTextBuffers[res];
121 gTextBuffers.editItemAt(res) = -1;
122
123 } else {
124 res = gTextBuffers.size();
125 gTextBuffers.add(-1);
126 }
127
128 mutex_unlock(&gMutex);
129
130 return res;
131 }
132
freeBufferIndex(int32_t idx)133 static void freeBufferIndex(int32_t idx)
134 {
135 mutex_lock(&gMutex);
136 gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
137 gFreeBufferIndex = idx;
138 mutex_unlock(&gMutex);
139 }
140
141 // ---------------------------------------------------------------------------
142
BufferedTextOutput(uint32_t flags)143 BufferedTextOutput::BufferedTextOutput(uint32_t flags)
144 : mFlags(flags)
145 , mSeq(android_atomic_inc(&gSequence))
146 , mIndex(allocBufferIndex())
147 {
148 mGlobalState = new BufferState(mSeq);
149 if (mGlobalState) mGlobalState->incStrong(this);
150 }
151
~BufferedTextOutput()152 BufferedTextOutput::~BufferedTextOutput()
153 {
154 if (mGlobalState) mGlobalState->decStrong(this);
155 freeBufferIndex(mIndex);
156 }
157
print(const char * txt,size_t len)158 status_t BufferedTextOutput::print(const char* txt, size_t len)
159 {
160 //printf("BufferedTextOutput: printing %d\n", len);
161
162 AutoMutex _l(mLock);
163 BufferState* b = getBuffer();
164
165 const char* const end = txt+len;
166
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
185 } else if (*(txt-1) == '\n' && !b->bundle) {
186 // Fast path: if we are not indenting or bundling, and
187 // have been given one or more complete lines, just write
188 // them out without going through the buffer.
189
190 // Slurp up all of the lines.
191 const char* lastLine = txt+1;
192 while (txt < end) {
193 if (*txt++ == '\n') lastLine = txt;
194 }
195 struct iovec vec;
196 vec.iov_base = (void*)first;
197 vec.iov_len = lastLine-first;
198 //printf("Writing %d bytes of data!\n", vec.iov_len);
199 writeLines(vec, 1);
200 txt = lastLine;
201 continue;
202 }
203 }
204
205 // Append the new text to the buffer.
206 err = b->append(first, txt-first);
207 if (err != NO_ERROR) return err;
208 b->atFront = *(txt-1) == '\n';
209
210 // If we have finished a line and are not bundling, write
211 // it out.
212 //printf("Buffer is now %d bytes\n", b->bufferPos);
213 if (b->atFront && !b->bundle) {
214 struct iovec vec;
215 vec.iov_base = b->buffer;
216 vec.iov_len = b->bufferPos;
217 //printf("Writing %d bytes of data!\n", vec.iov_len);
218 writeLines(vec, 1);
219 b->restart();
220 }
221 }
222
223 return NO_ERROR;
224 }
225
moveIndent(int delta)226 void BufferedTextOutput::moveIndent(int delta)
227 {
228 AutoMutex _l(mLock);
229 BufferState* b = getBuffer();
230 b->indent += delta;
231 if (b->indent < 0) b->indent = 0;
232 }
233
pushBundle()234 void BufferedTextOutput::pushBundle()
235 {
236 AutoMutex _l(mLock);
237 BufferState* b = getBuffer();
238 b->bundle++;
239 }
240
popBundle()241 void BufferedTextOutput::popBundle()
242 {
243 AutoMutex _l(mLock);
244 BufferState* b = getBuffer();
245 b->bundle--;
246 LOG_FATAL_IF(b->bundle < 0,
247 "TextOutput::popBundle() called more times than pushBundle()");
248 if (b->bundle < 0) b->bundle = 0;
249
250 if (b->bundle == 0) {
251 // Last bundle, write out data if it is complete. If it is not
252 // complete, don't write until the last line is done... this may
253 // or may not be the write thing to do, but it's the easiest.
254 if (b->bufferPos > 0 && b->atFront) {
255 struct iovec vec;
256 vec.iov_base = b->buffer;
257 vec.iov_len = b->bufferPos;
258 writeLines(vec, 1);
259 b->restart();
260 }
261 }
262 }
263
getBuffer() const264 BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
265 {
266 if ((mFlags&MULTITHREADED) != 0) {
267 ThreadState* ts = getThreadState();
268 if (ts) {
269 while (ts->states.size() <= (size_t)mIndex) ts->states.add(NULL);
270 BufferState* bs = ts->states[mIndex].get();
271 if (bs != NULL && bs->seq == mSeq) return bs;
272
273 ts->states.editItemAt(mIndex) = new BufferState(mIndex);
274 bs = ts->states[mIndex].get();
275 if (bs != NULL) return bs;
276 }
277 }
278
279 return mGlobalState;
280 }
281
282 }; // namespace android
283