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/BufferedTextOutput.h> 20 #include <hwbinder/Debug.h> 21 22 #include <cutils/atomic.h> 23 #include <cutils/threads.h> 24 #include <utils/Log.h> 25 #include <utils/RefBase.h> 26 #include <utils/Vector.h> 27 28 #include <hwbinder/Static.h> 29 30 #include <pthread.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 34 // --------------------------------------------------------------------------- 35 36 namespace android { 37 namespace hardware { 38 39 struct BufferedTextOutput::BufferState : public RefBase 40 { 41 explicit BufferState(int32_t _seq) 42 : seq(_seq) 43 , buffer(nullptr) 44 , bufferPos(0) 45 , bufferSize(0) 46 , atFront(true) 47 , indent(0) 48 , bundle(0) { 49 } 50 ~BufferState() { 51 free(buffer); 52 } 53 54 status_t append(const char* txt, size_t len) { 55 if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow 56 if ((len+bufferPos) > bufferSize) { 57 if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow 58 size_t newSize = ((len+bufferPos)*3)/2; 59 void* b = realloc(buffer, newSize); 60 if (!b) return NO_MEMORY; 61 buffer = (char*)b; 62 bufferSize = newSize; 63 } 64 memcpy(buffer+bufferPos, txt, len); 65 bufferPos += len; 66 return NO_ERROR; 67 } 68 69 void restart() { 70 bufferPos = 0; 71 atFront = true; 72 if (bufferSize > 256) { 73 void* b = realloc(buffer, 256); 74 if (b) { 75 buffer = (char*)b; 76 bufferSize = 256; 77 } 78 } 79 } 80 81 const int32_t seq; 82 char* buffer; 83 size_t bufferPos; 84 size_t bufferSize; 85 bool atFront; 86 int32_t indent; 87 int32_t bundle; 88 }; 89 90 struct BufferedTextOutput::ThreadState 91 { 92 Vector<sp<BufferedTextOutput::BufferState> > states; 93 }; 94 95 static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER; 96 97 static thread_store_t tls; 98 99 BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState() 100 { 101 ThreadState* ts = (ThreadState*) thread_store_get( &tls ); 102 if (ts) return ts; 103 ts = new ThreadState; 104 thread_store_set( &tls, ts, threadDestructor ); 105 return ts; 106 } 107 108 void BufferedTextOutput::threadDestructor(void *st) 109 { 110 delete ((ThreadState*)st); 111 } 112 113 static volatile int32_t gSequence = 0; 114 115 static volatile int32_t gFreeBufferIndex = -1; 116 117 static int32_t allocBufferIndex() 118 { 119 int32_t res = -1; 120 121 pthread_mutex_lock(&gMutex); 122 123 if (gFreeBufferIndex >= 0) { 124 res = gFreeBufferIndex; 125 gFreeBufferIndex = gTextBuffers[res]; 126 gTextBuffers.editItemAt(res) = -1; 127 128 } else { 129 res = gTextBuffers.size(); 130 gTextBuffers.add(-1); 131 } 132 133 pthread_mutex_unlock(&gMutex); 134 135 return res; 136 } 137 138 static void freeBufferIndex(int32_t idx) 139 { 140 pthread_mutex_lock(&gMutex); 141 gTextBuffers.editItemAt(idx) = gFreeBufferIndex; 142 gFreeBufferIndex = idx; 143 pthread_mutex_unlock(&gMutex); 144 } 145 146 // --------------------------------------------------------------------------- 147 148 BufferedTextOutput::BufferedTextOutput(uint32_t flags) 149 : mFlags(flags) 150 , mSeq(android_atomic_inc(&gSequence)) 151 , mIndex(allocBufferIndex()) 152 { 153 mGlobalState = new BufferState(mSeq); 154 if (mGlobalState) mGlobalState->incStrong(this); 155 } 156 157 BufferedTextOutput::~BufferedTextOutput() 158 { 159 if (mGlobalState) mGlobalState->decStrong(this); 160 freeBufferIndex(mIndex); 161 } 162 163 status_t BufferedTextOutput::print(const char* txt, size_t len) 164 { 165 AutoMutex _l(mLock); 166 BufferState* b = getBuffer(); 167 const char* const end = txt+len; 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 } 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 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 234 void BufferedTextOutput::pushBundle() 235 { 236 AutoMutex _l(mLock); 237 BufferState* b = getBuffer(); 238 b->bundle++; 239 } 240 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 264 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(nullptr); 270 BufferState* bs = ts->states[mIndex].get(); 271 if (bs != nullptr && bs->seq == mSeq) return bs; 272 273 ts->states.editItemAt(mIndex) = new BufferState(mIndex); 274 bs = ts->states[mIndex].get(); 275 if (bs != nullptr) return bs; 276 } 277 } 278 279 return mGlobalState; 280 } 281 282 } // namespace hardware 283 } // namespace android 284