• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "BufferedTextOutput.h"
18 
19 #include <cutils/atomic.h>
20 #include <utils/Log.h>
21 #include <utils/RefBase.h>
22 #include <utils/Vector.h>
23 
24 #include <pthread.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 
28 #include "Debug.h"
29 #include "Static.h"
30 
31 // ---------------------------------------------------------------------------
32 
33 namespace android {
34 
35 struct BufferedTextOutput::BufferState : public RefBase
36 {
BufferStateandroid::BufferedTextOutput::BufferState37     explicit BufferState(int32_t _seq)
38         : seq(_seq)
39         , buffer(nullptr)
40         , bufferPos(0)
41         , bufferSize(0)
42         , atFront(true)
43         , indent(0)
44         , bundle(0) {
45     }
~BufferStateandroid::BufferedTextOutput::BufferState46     ~BufferState() {
47         free(buffer);
48     }
49 
appendandroid::BufferedTextOutput::BufferState50     status_t append(const char* txt, size_t len) {
51         if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
52         if ((len+bufferPos) > bufferSize) {
53             if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
54             size_t newSize = ((len+bufferPos)*3)/2;
55             void* b = realloc(buffer, newSize);
56             if (!b) return NO_MEMORY;
57             buffer = (char*)b;
58             bufferSize = newSize;
59         }
60         memcpy(buffer+bufferPos, txt, len);
61         bufferPos += len;
62         return NO_ERROR;
63     }
64 
restartandroid::BufferedTextOutput::BufferState65     void restart() {
66         bufferPos = 0;
67         atFront = true;
68         if (bufferSize > 256) {
69             void* b = realloc(buffer, 256);
70             if (b) {
71                 buffer = (char*)b;
72                 bufferSize = 256;
73             }
74         }
75     }
76 
77     const int32_t seq;
78     char* buffer;
79     size_t bufferPos;
80     size_t bufferSize;
81     bool atFront;
82     int32_t indent;
83     int32_t bundle;
84 };
85 
86 struct BufferedTextOutput::ThreadState
87 {
88     Vector<sp<BufferedTextOutput::BufferState> > states;
89 };
90 
91 static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
92 
93 static volatile int32_t gSequence = 0;
94 
95 static volatile int32_t gFreeBufferIndex = -1;
96 
allocBufferIndex()97 static int32_t allocBufferIndex()
98 {
99     int32_t res = -1;
100 
101     pthread_mutex_lock(&gMutex);
102 
103     if (gFreeBufferIndex >= 0) {
104         res = gFreeBufferIndex;
105         gFreeBufferIndex = gTextBuffers[res];
106         gTextBuffers.editItemAt(res) = -1;
107 
108     } else {
109         res = gTextBuffers.size();
110         gTextBuffers.add(-1);
111     }
112 
113     pthread_mutex_unlock(&gMutex);
114 
115     return res;
116 }
117 
freeBufferIndex(int32_t idx)118 static void freeBufferIndex(int32_t idx)
119 {
120     pthread_mutex_lock(&gMutex);
121     gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
122     gFreeBufferIndex = idx;
123     pthread_mutex_unlock(&gMutex);
124 }
125 
126 // ---------------------------------------------------------------------------
127 
BufferedTextOutput(uint32_t flags)128 BufferedTextOutput::BufferedTextOutput(uint32_t flags)
129     : mFlags(flags)
130     , mSeq(android_atomic_inc(&gSequence))
131     , mIndex(allocBufferIndex())
132 {
133     mGlobalState = new BufferState(mSeq);
134     if (mGlobalState) mGlobalState->incStrong(this);
135 }
136 
~BufferedTextOutput()137 BufferedTextOutput::~BufferedTextOutput()
138 {
139     if (mGlobalState) mGlobalState->decStrong(this);
140     freeBufferIndex(mIndex);
141 }
142 
print(const char * txt,size_t len)143 status_t BufferedTextOutput::print(const char* txt, size_t len)
144 {
145     //printf("BufferedTextOutput: printing %d\n", len);
146 
147     AutoMutex _l(mLock);
148     BufferState* b = getBuffer();
149 
150     const char* const end = txt+len;
151 
152     status_t err;
153 
154     while (txt < end) {
155         // Find the next line.
156         const char* first = txt;
157         while (txt < end && *txt != '\n') txt++;
158 
159         // Include this and all following empty lines.
160         while (txt < end && *txt == '\n') txt++;
161 
162         // Special cases for first data on a line.
163         if (b->atFront) {
164             if (b->indent > 0) {
165                 // If this is the start of a line, add the indent.
166                 const char* prefix = stringForIndent(b->indent);
167                 err = b->append(prefix, strlen(prefix));
168                 if (err != NO_ERROR) return err;
169 
170             } else if (*(txt-1) == '\n' && !b->bundle) {
171                 // Fast path: if we are not indenting or bundling, and
172                 // have been given one or more complete lines, just write
173                 // them out without going through the buffer.
174 
175                 // Slurp up all of the lines.
176                 const char* lastLine = txt;
177                 while (txt < end) {
178                     if (*txt++ == '\n') lastLine = txt;
179                 }
180                 struct iovec vec;
181                 vec.iov_base = (void*)first;
182                 vec.iov_len = lastLine-first;
183                 //printf("Writing %d bytes of data!\n", vec.iov_len);
184                 writeLines(vec, 1);
185                 txt = lastLine;
186                 continue;
187             }
188         }
189 
190         // Append the new text to the buffer.
191         err = b->append(first, txt-first);
192         if (err != NO_ERROR) return err;
193         b->atFront = *(txt-1) == '\n';
194 
195         // If we have finished a line and are not bundling, write
196         // it out.
197         //printf("Buffer is now %d bytes\n", b->bufferPos);
198         if (b->atFront && !b->bundle) {
199             struct iovec vec;
200             vec.iov_base = b->buffer;
201             vec.iov_len = b->bufferPos;
202             //printf("Writing %d bytes of data!\n", vec.iov_len);
203             writeLines(vec, 1);
204             b->restart();
205         }
206     }
207 
208     return NO_ERROR;
209 }
210 
moveIndent(int delta)211 void BufferedTextOutput::moveIndent(int delta)
212 {
213     AutoMutex _l(mLock);
214     BufferState* b = getBuffer();
215     b->indent += delta;
216     if (b->indent < 0) b->indent = 0;
217 }
218 
pushBundle()219 void BufferedTextOutput::pushBundle()
220 {
221     AutoMutex _l(mLock);
222     BufferState* b = getBuffer();
223     b->bundle++;
224 }
225 
popBundle()226 void BufferedTextOutput::popBundle()
227 {
228     AutoMutex _l(mLock);
229     BufferState* b = getBuffer();
230     b->bundle--;
231     LOG_FATAL_IF(b->bundle < 0,
232         "TextOutput::popBundle() called more times than pushBundle()");
233     if (b->bundle < 0) b->bundle = 0;
234 
235     if (b->bundle == 0) {
236         // Last bundle, write out data if it is complete.  If it is not
237         // complete, don't write until the last line is done... this may
238         // or may not be the write thing to do, but it's the easiest.
239         if (b->bufferPos > 0 && b->atFront) {
240             struct iovec vec;
241             vec.iov_base = b->buffer;
242             vec.iov_len = b->bufferPos;
243             writeLines(vec, 1);
244             b->restart();
245         }
246     }
247 }
248 
getBuffer() const249 BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
250 {
251     if ((mFlags&MULTITHREADED) != 0) {
252         thread_local ThreadState ts;
253         while (ts.states.size() <= (size_t)mIndex) ts.states.add(nullptr);
254         BufferState* bs = ts.states[mIndex].get();
255         if (bs != nullptr && bs->seq == mSeq) return bs;
256 
257         ts.states.editItemAt(mIndex) = sp<BufferState>::make(mIndex);
258         bs = ts.states[mIndex].get();
259         if (bs != nullptr) return bs;
260     }
261 
262     return mGlobalState;
263 }
264 
265 } // namespace android
266