blob: a90bfd29badcf8c5c6dde7ee8d56e96e50687cf4 [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"
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080018
Steven Moreland2716e112018-02-23 14:57:20 -080019#include <cutils/atomic.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080020#include <utils/Log.h>
21#include <utils/RefBase.h>
22#include <utils/Vector.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080023
Elliott Hughes5bf516f2018-07-13 11:13:42 -070024#include <pthread.h>
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080025#include <stdio.h>
26#include <stdlib.h>
27
Steven Morelandaaff3252021-03-06 00:26:09 +000028#include "Debug.h"
Steven Morelanda4853cd2019-07-12 15:44:37 -070029#include "Static.h"
30
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080031// ---------------------------------------------------------------------------
32
33namespace android {
34
35struct BufferedTextOutput::BufferState : public RefBase
36{
Chih-Hung Hsiehe2347b72016-04-25 15:41:05 -070037 explicit BufferState(int32_t _seq)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080038 : seq(_seq)
Yi Kongfdd8da92018-06-07 17:52:27 -070039 , buffer(nullptr)
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080040 , 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) {
Martijn Coenen93fe5182020-01-22 10:46:25 +010051 if (len > SIZE_MAX - bufferPos) return NO_MEMORY; // overflow
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080052 if ((len+bufferPos) > bufferSize) {
Martijn Coenen93fe5182020-01-22 10:46:25 +010053 if ((len + bufferPos) > SIZE_MAX / 3) return NO_MEMORY; // overflow
Christopher Tateed7a50c2015-06-08 14:45:14 -070054 size_t newSize = ((len+bufferPos)*3)/2;
Christopher Tateed7a50c2015-06-08 14:45:14 -070055 void* b = realloc(buffer, newSize);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080056 if (!b) return NO_MEMORY;
57 buffer = (char*)b;
Christopher Tateed7a50c2015-06-08 14:45:14 -070058 bufferSize = newSize;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080059 }
60 memcpy(buffer+bufferPos, txt, len);
61 bufferPos += len;
62 return NO_ERROR;
63 }
64
65 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
86struct BufferedTextOutput::ThreadState
87{
88 Vector<sp<BufferedTextOutput::BufferState> > states;
89};
90
Elliott Hughes5bf516f2018-07-13 11:13:42 -070091static pthread_mutex_t gMutex = PTHREAD_MUTEX_INITIALIZER;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080092
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -080093static volatile int32_t gSequence = 0;
94
95static volatile int32_t gFreeBufferIndex = -1;
96
97static int32_t allocBufferIndex()
98{
99 int32_t res = -1;
100
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700101 pthread_mutex_lock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800102
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
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700113 pthread_mutex_unlock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800114
115 return res;
116}
117
118static void freeBufferIndex(int32_t idx)
119{
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700120 pthread_mutex_lock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800121 gTextBuffers.editItemAt(idx) = gFreeBufferIndex;
122 gFreeBufferIndex = idx;
Elliott Hughes5bf516f2018-07-13 11:13:42 -0700123 pthread_mutex_unlock(&gMutex);
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800124}
125
126// ---------------------------------------------------------------------------
127
128BufferedTextOutput::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
137BufferedTextOutput::~BufferedTextOutput()
138{
139 if (mGlobalState) mGlobalState->decStrong(this);
140 freeBufferIndex(mIndex);
141}
142
143status_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.
Christopher Ferris12fe72b2018-08-31 14:13:51 -0700176 const char* lastLine = txt;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800177 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
211void 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
219void BufferedTextOutput::pushBundle()
220{
221 AutoMutex _l(mLock);
222 BufferState* b = getBuffer();
223 b->bundle++;
224}
225
226void 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
249BufferedTextOutput::BufferState* BufferedTextOutput::getBuffer() const
250{
251 if ((mFlags&MULTITHREADED) != 0) {
Elliott Hughes308e9802020-12-03 15:42:02 -0800252 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
Steven Moreland1a3a8ef2021-04-02 02:52:46 +0000257 ts.states.editItemAt(mIndex) = sp<BufferState>::make(mIndex);
Elliott Hughes308e9802020-12-03 15:42:02 -0800258 bs = ts.states[mIndex].get();
259 if (bs != nullptr) return bs;
The Android Open Source Projectedbf3b62009-03-03 19:31:44 -0800260 }
261
262 return mGlobalState;
263}
264
Steven Moreland61ff8492019-09-26 16:05:45 -0700265} // namespace android