Home | History | Annotate | Download | only in binder
      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