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