blob: 8cf609775900ce6143a988e2b2b0854344bf2dea [file] [log] [blame]
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -08001/*
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
Steven Moreland6c7d2142020-01-30 14:39:17 -080017#include "BufferedTextOutput.h"
Mathias Agopian002e1e52013-05-06 20:20:50 -070018#include <binder/Debug.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080019
Steven Moreland2716e112018-02-23 14:57:20 -080020#include <cutils/atomic.h>
21#include <cutils/threads.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080022#include <utils/Log.h>
23#include <utils/RefBase.h>
24#include <utils/Vector.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080025
Elliott Hughes5bf516f2018-07-13 11:13:42 -070026#include <pthread.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080027#include <stdio.h>
28#include <stdlib.h>
29
Steven Morelanda4853cd2019-07-12 15:44:37 -070030#include "Static.h"
31
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080032// ---------------------------------------------------------------------------
33
34namespace android {
35
36struct BufferedTextOutput::BufferState : public RefBase
37{
Chih-Hung Hsiehe2347b72016-04-25 15:41:05 -070038 explicit BufferState(int32_t _seq)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080039 : seq(_seq)
Yi Kongfdd8da92018-06-07 17:52:27 -070040 , buffer(nullptr)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080041 , bufferPos(0)
42 , bufferSize(0)
43 , atFront(true)
44 , indent(0)
45 , bundle(0) {
46 }
47 ~BufferState() {
48 free(buffer);
49 }
50
51 status_t append(const char* txt, size_t len) {
Martijn Coenenda2f2fd2020-01-22 10:46:25 +010052 if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080053 if ((len+bufferPos) > bufferSize) {
Martijn Coenenda2f2fd2020-01-22 10:46:25 +010054 if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
Christopher Tateed7a50c2015-06-08 14:45:14 -070055 size_t newSize = ((len+bufferPos)*3)/2;
Christopher Tateed7a50c2015-06-08 14:45:14 -070056 void* b = realloc(buffer, newSize);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080057 if (!b) return NO_MEMORY;
58 buffer = (char*)b;
Christopher Tateed7a50c2015-06-08 14:45:14 -070059 bufferSize = newSize;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080060 }
61 memcpy(buffer+bufferPos, txt, len);
62 bufferPos += len;
63 return NO_ERROR;
64 }
65
66 void restart() {
67 bufferPos = 0;
68 atFront = true;
69 if (bufferSize > 256) {
70 void* b = realloc(buffer, 256);
71 if (b) {
72 buffer = (char*)b;
73 bufferSize = 256;
74 }
75 }
76 }
77
78 const int32_t seq;
79 char* buffer;
80 size_t bufferPos;
81 size_t bufferSize;
82 bool atFront;
83 int32_t indent;
84 int32_t bundle;
85};
86
87struct BufferedTextOutput::ThreadState
88{
89 Vector<sp<BufferedTextOutput::BufferState> > states;
90};
91
Elliott Hughes5bf516f2018-07-13 11:13:42 -070092static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080093
94static thread_store_t tls;
95
96BufferedTextOutput::ThreadState* BufferedTextOutput::getThreadState()
97{
98 ThreadState* ts = (ThreadState*) thread_store_get( &tls );
99 if (ts) return ts;
100 ts = new ThreadState;
101 thread_store_set( &tls, ts, threadDestructor );
102 return ts;
103}
104
105void BufferedTextOutput::threadDestructor(void *st)
106{
107 delete ((ThreadState*)st);
108}
109
110static volatile int32_t gSequence = 0;
111
112static volatile int32_t gFreeBufferIndex = -1;
113
114static int32_t allocBufferIndex()
115{
116 int32_t res = -1;
117
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700118 pthread_mutex_lock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800119
120 if (gFreeBufferIndex >= 0) {
121 res = gFreeBufferIndex;
122 gFreeBufferIndex = gTextBuffers[res];
123 gTextBuffers.editItemAt(res) = -1;
124
125 } else {
126 res = gTextBuffers.size();
127 gTextBuffers.add(-1);
128 }
129
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700130 pthread_mutex_unlock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800131
132 return res;
133}
134
135static void freeBufferIndex(int32_t idx)
136{
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700137 pthread_mutex_lock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800138 gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
139 gFreeBufferIndex = idx;
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700140 pthread_mutex_unlock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800141}
142
143// ---------------------------------------------------------------------------
144
145BufferedTextOutput::BufferedTextOutput(uint32_t flags)
146 : mFlags(flags)
147 , mSeq(android_atomic_inc(&gSequence))
148 , mIndex(allocBufferIndex())
149{
150 mGlobalState = new BufferState(mSeq);
151 if (mGlobalState) mGlobalState->incStrong(this);
152}
153
154BufferedTextOutput::~BufferedTextOutput()
155{
156 if (mGlobalState) mGlobalState->decStrong(this);
157 freeBufferIndex(mIndex);
158}
159
160status_t BufferedTextOutput::print(const char* txt, size_t len)
161{
162 //printf("BufferedTextOutput: printing %d\n", len);
163
164 AutoMutex _l(mLock);
165 BufferState* b = getBuffer();
166
167 const char* const end = txt+len;
168
169 status_t err;
170
171 while (txt < end) {
172 // Find the next line.
173 const char* first = txt;
174 while (txt < end && *txt != '\n') txt++;
175
176 // Include this and all following empty lines.
177 while (txt < end && *txt == '\n') txt++;
178
179 // Special cases for first data on a line.
180 if (b->atFront) {
181 if (b->indent > 0) {
182 // If this is the start of a line, add the indent.
183 const char* prefix = stringForIndent(b->indent);
184 err = b->append(prefix, strlen(prefix));
185 if (err != NO_ERROR) return err;
186
187 } else if (*(txt-1) == '\n' && !b->bundle) {
188 // Fast path: if we are not indenting or bundling, and
189 // have been given one or more complete lines, just write
190 // them out without going through the buffer.
191
192 // Slurp up all of the lines.
Christopher Ferris12fe72b2018-08-31 14:13:51 -0700193 const char* lastLine = txt;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800194 while (txt < end) {
195 if (*txt++ == '\n') lastLine = txt;
196 }
197 struct iovec vec;
198 vec.iov_base = (void*)first;
199 vec.iov_len = lastLine-first;
200 //printf("Writing %d bytes of data!\n", vec.iov_len);
201 writeLines(vec, 1);
202 txt = lastLine;
203 continue;
204 }
205 }
206
207 // Append the new text to the buffer.
208 err = b->append(first, txt-first);
209 if (err != NO_ERROR) return err;
210 b->atFront = *(txt-1) == '\n';
211
212 // If we have finished a line and are not bundling, write
213 // it out.
214 //printf("Buffer is now %d bytes\n", b->bufferPos);
215 if (b->atFront && !b->bundle) {
216 struct iovec vec;
217 vec.iov_base = b->buffer;
218 vec.iov_len = b->bufferPos;
219 //printf("Writing %d bytes of data!\n", vec.iov_len);
220 writeLines(vec, 1);
221 b->restart();
222 }
223 }
224
225 return NO_ERROR;
226}
227
228void BufferedTextOutput::moveIndent(int delta)
229{
230 AutoMutex _l(mLock);
231 BufferState* b = getBuffer();
232 b->indent += delta;
233 if (b->indent < 0) b->indent = 0;
234}
235
236void BufferedTextOutput::pushBundle()
237{
238 AutoMutex _l(mLock);
239 BufferState* b = getBuffer();
240 b->bundle++;
241}
242
243void BufferedTextOutput::popBundle()
244{
245 AutoMutex _l(mLock);
246 BufferState* b = getBuffer();
247 b->bundle--;
248 LOG_FATAL_IF(b->bundle < 0,
249 "TextOutput::popBundle() called more times than pushBundle()");
250 if (b->bundle < 0) b->bundle = 0;
251
252 if (b->bundle == 0) {
253 // Last bundle, write out data if it is complete. If it is not
254 // complete, don't write until the last line is done... this may
255 // or may not be the write thing to do, but it's the easiest.
256 if (b->bufferPos > 0 && b->atFront) {
257 struct iovec vec;
258 vec.iov_base = b->buffer;
259 vec.iov_len = b->bufferPos;
260 writeLines(vec, 1);
261 b->restart();
262 }
263 }
264}
265
266BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
267{
268 if ((mFlags&MULTITHREADED) != 0) {
269 ThreadState* ts = getThreadState();
270 if (ts) {
Yi Kongfdd8da92018-06-07 17:52:27 -0700271 while (ts->states.size() <= (size_t)mIndex) ts->states.add(nullptr);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800272 BufferState* bs = ts->states[mIndex].get();
Yi Kongfdd8da92018-06-07 17:52:27 -0700273 if (bs != nullptr && bs->seq == mSeq) return bs;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800274
275 ts->states.editItemAt(mIndex) = new BufferState(mIndex);
276 bs = ts->states[mIndex].get();
Yi Kongfdd8da92018-06-07 17:52:27 -0700277 if (bs != nullptr) return bs;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800278 }
279 }
280
281 return mGlobalState;
282}
283
Steven Moreland6511af52019-09-26 16:05:45 -0700284} // namespace android