• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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