• 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 <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