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