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