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