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     explicit 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