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 { 37 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 } 46 ~BufferState() { 47 free(buffer); 48 } 49 50 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 64 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 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 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 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 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 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 152 BufferedTextOutput::~BufferedTextOutput() 153 { 154 if (mGlobalState) mGlobalState->decStrong(this); 155 freeBufferIndex(mIndex); 156 } 157 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 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(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