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