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